1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either of the GNU General Public License Version 2 or later (the "GPL"),
25 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsHTMLMenuElement.h"
38 :
39 : #include "nsIDOMHTMLMenuItemElement.h"
40 : #include "nsXULContextMenuBuilder.h"
41 : #include "nsGUIEvent.h"
42 : #include "nsEventDispatcher.h"
43 : #include "nsHTMLMenuItemElement.h"
44 : #include "nsContentUtils.h"
45 :
46 : enum MenuType
47 : {
48 : MENU_TYPE_CONTEXT = 1,
49 : MENU_TYPE_TOOLBAR,
50 : MENU_TYPE_LIST
51 : };
52 :
53 : static const nsAttrValue::EnumTable kMenuTypeTable[] = {
54 : { "context", MENU_TYPE_CONTEXT },
55 : { "toolbar", MENU_TYPE_TOOLBAR },
56 : { "list", MENU_TYPE_LIST },
57 : { 0 }
58 : };
59 :
60 : static const nsAttrValue::EnumTable* kMenuDefaultType =
61 : &kMenuTypeTable[2];
62 :
63 : enum SeparatorType
64 : {
65 : ST_TRUE_INIT = -1,
66 : ST_FALSE = 0,
67 : ST_TRUE = 1
68 : };
69 :
70 0 : NS_IMPL_NS_NEW_HTML_ELEMENT(Menu)
71 :
72 :
73 0 : nsHTMLMenuElement::nsHTMLMenuElement(already_AddRefed<nsINodeInfo> aNodeInfo)
74 0 : : nsGenericHTMLElement(aNodeInfo), mType(MENU_TYPE_LIST)
75 : {
76 0 : }
77 :
78 0 : nsHTMLMenuElement::~nsHTMLMenuElement()
79 : {
80 0 : }
81 :
82 :
83 0 : NS_IMPL_ADDREF_INHERITED(nsHTMLMenuElement, nsGenericElement)
84 0 : NS_IMPL_RELEASE_INHERITED(nsHTMLMenuElement, nsGenericElement)
85 :
86 :
87 0 : DOMCI_NODE_DATA(HTMLMenuElement, nsHTMLMenuElement)
88 :
89 : // QueryInterface implementation for nsHTMLMenuElement
90 0 : NS_INTERFACE_TABLE_HEAD(nsHTMLMenuElement)
91 0 : NS_HTML_CONTENT_INTERFACE_TABLE2(nsHTMLMenuElement,
92 : nsIDOMHTMLMenuElement,
93 : nsIHTMLMenu)
94 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLMenuElement,
95 : nsGenericHTMLElement)
96 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLMenuElement)
97 :
98 0 : NS_IMPL_ELEMENT_CLONE(nsHTMLMenuElement)
99 :
100 0 : NS_IMPL_BOOL_ATTR(nsHTMLMenuElement, Compact, compact)
101 0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLMenuElement, Type, type,
102 : kMenuDefaultType->tag)
103 0 : NS_IMPL_STRING_ATTR(nsHTMLMenuElement, Label, label)
104 :
105 :
106 : NS_IMETHODIMP
107 0 : nsHTMLMenuElement::SendShowEvent()
108 : {
109 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
110 :
111 0 : nsCOMPtr<nsIDocument> document = GetCurrentDoc();
112 0 : if (!document) {
113 0 : return NS_ERROR_FAILURE;
114 : }
115 :
116 0 : nsEvent event(true, NS_SHOW_EVENT);
117 0 : event.flags |= NS_EVENT_FLAG_CANT_CANCEL | NS_EVENT_FLAG_CANT_BUBBLE;
118 :
119 0 : nsCOMPtr<nsIPresShell> shell = document->GetShell();
120 0 : if (!shell) {
121 0 : return NS_ERROR_FAILURE;
122 : }
123 :
124 0 : nsRefPtr<nsPresContext> presContext = shell->GetPresContext();
125 0 : nsEventStatus status = nsEventStatus_eIgnore;
126 : nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
127 0 : &event, nsnull, &status);
128 :
129 0 : return NS_OK;
130 : }
131 :
132 : NS_IMETHODIMP
133 0 : nsHTMLMenuElement::CreateBuilder(nsIMenuBuilder** _retval)
134 : {
135 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
136 :
137 0 : *_retval = nsnull;
138 :
139 0 : if (mType == MENU_TYPE_CONTEXT) {
140 0 : NS_ADDREF(*_retval = new nsXULContextMenuBuilder());
141 : }
142 :
143 0 : return NS_OK;
144 : }
145 :
146 :
147 : NS_IMETHODIMP
148 0 : nsHTMLMenuElement::Build(nsIMenuBuilder* aBuilder)
149 : {
150 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
151 :
152 0 : if (!aBuilder) {
153 0 : return NS_OK;
154 : }
155 :
156 0 : BuildSubmenu(EmptyString(), this, aBuilder);
157 :
158 0 : return NS_OK;
159 : }
160 :
161 :
162 : bool
163 0 : nsHTMLMenuElement::ParseAttribute(PRInt32 aNamespaceID,
164 : nsIAtom* aAttribute,
165 : const nsAString& aValue,
166 : nsAttrValue& aResult)
167 : {
168 0 : if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::type) {
169 : bool success = aResult.ParseEnumValue(aValue, kMenuTypeTable,
170 0 : false);
171 0 : if (success) {
172 0 : mType = aResult.GetEnumValue();
173 : } else {
174 0 : mType = kMenuDefaultType->value;
175 : }
176 :
177 0 : return success;
178 : }
179 :
180 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
181 0 : aResult);
182 : }
183 :
184 : void
185 0 : nsHTMLMenuElement::BuildSubmenu(const nsAString& aLabel,
186 : nsIContent* aContent,
187 : nsIMenuBuilder* aBuilder)
188 : {
189 0 : aBuilder->OpenContainer(aLabel);
190 :
191 0 : PRInt8 separator = ST_TRUE_INIT;
192 0 : TraverseContent(aContent, aBuilder, separator);
193 :
194 0 : if (separator == ST_TRUE) {
195 0 : aBuilder->UndoAddSeparator();
196 : }
197 :
198 0 : aBuilder->CloseContainer();
199 0 : }
200 :
201 : // static
202 : bool
203 0 : nsHTMLMenuElement::CanLoadIcon(nsIContent* aContent, const nsAString& aIcon)
204 : {
205 0 : if (aIcon.IsEmpty()) {
206 0 : return false;
207 : }
208 :
209 0 : nsIDocument* doc = aContent->OwnerDoc();
210 :
211 0 : nsCOMPtr<nsIURI> baseURI = aContent->GetBaseURI();
212 0 : nsCOMPtr<nsIURI> uri;
213 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), aIcon, doc,
214 0 : baseURI);
215 :
216 0 : if (!uri) {
217 0 : return false;
218 : }
219 :
220 : return nsContentUtils::CanLoadImage(uri, aContent, doc,
221 0 : aContent->NodePrincipal());
222 : }
223 :
224 : void
225 0 : nsHTMLMenuElement::TraverseContent(nsIContent* aContent,
226 : nsIMenuBuilder* aBuilder,
227 : PRInt8& aSeparator)
228 : {
229 0 : nsCOMPtr<nsIContent> child;
230 0 : for (child = aContent->GetFirstChild(); child;
231 0 : child = child->GetNextSibling()) {
232 0 : nsGenericHTMLElement* element = nsGenericHTMLElement::FromContent(child);
233 0 : if (!element) {
234 0 : continue;
235 : }
236 :
237 0 : nsIAtom* tag = child->Tag();
238 :
239 0 : if (tag == nsGkAtoms::menuitem) {
240 : nsHTMLMenuItemElement* menuitem =
241 0 : nsHTMLMenuItemElement::FromContent(child);
242 :
243 0 : if (menuitem->IsHidden()) {
244 0 : continue;
245 : }
246 :
247 0 : nsAutoString label;
248 0 : menuitem->GetLabel(label);
249 0 : if (label.IsEmpty()) {
250 0 : continue;
251 : }
252 :
253 0 : nsAutoString icon;
254 0 : menuitem->GetIcon(icon);
255 :
256 0 : aBuilder->AddItemFor(menuitem, CanLoadIcon(child, icon));
257 :
258 0 : aSeparator = ST_FALSE;
259 0 : } else if (tag == nsGkAtoms::menu && !element->IsHidden()) {
260 0 : if (child->HasAttr(kNameSpaceID_None, nsGkAtoms::label)) {
261 0 : nsAutoString label;
262 0 : child->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
263 :
264 0 : BuildSubmenu(label, child, aBuilder);
265 :
266 0 : aSeparator = ST_FALSE;
267 : } else {
268 0 : AddSeparator(aBuilder, aSeparator);
269 :
270 0 : TraverseContent(child, aBuilder, aSeparator);
271 :
272 0 : AddSeparator(aBuilder, aSeparator);
273 : }
274 : }
275 : }
276 0 : }
277 :
278 : inline void
279 0 : nsHTMLMenuElement::AddSeparator(nsIMenuBuilder* aBuilder, PRInt8& aSeparator)
280 : {
281 0 : if (aSeparator) {
282 0 : return;
283 : }
284 :
285 0 : aBuilder->AddSeparator();
286 0 : aSeparator = ST_TRUE;
287 : }
|