Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/HTMLMenuElement.cpp
Line
Count
Source (jump to first uncovered line)
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
0
#define HTMLMENUBUILDER_CONTRACTID "@mozilla.org/content/html-menu-builder;1"
19
20
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
HTMLMenuElement::HTMLMenuElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
50
  : nsGenericHTMLElement(std::move(aNodeInfo)), mType(MENU_TYPE_TOOLBAR)
51
0
{
52
0
}
53
54
HTMLMenuElement::~HTMLMenuElement()
55
0
{
56
0
}
57
58
NS_IMPL_ELEMENT_CLONE(HTMLMenuElement)
59
60
61
void
62
HTMLMenuElement::SendShowEvent()
63
0
{
64
0
  nsCOMPtr<nsIDocument> document = GetComposedDoc();
65
0
  if (!document) {
66
0
    return;
67
0
  }
68
0
69
0
  WidgetEvent event(true, eShow);
70
0
  event.mFlags.mBubbles = false;
71
0
  event.mFlags.mCancelable = false;
72
0
73
0
  RefPtr<nsPresContext> presContext = document->GetPresContext();
74
0
  if (!presContext) {
75
0
    return;
76
0
  }
77
0
78
0
  nsEventStatus status = nsEventStatus_eIgnore;
79
0
  EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
80
0
                            &event, nullptr, &status);
81
0
}
82
83
already_AddRefed<nsIMenuBuilder>
84
HTMLMenuElement::CreateBuilder()
85
0
{
86
0
  if (mType != MENU_TYPE_CONTEXT) {
87
0
    return nullptr;
88
0
  }
89
0
90
0
  nsCOMPtr<nsIMenuBuilder> builder = do_CreateInstance(HTMLMENUBUILDER_CONTRACTID);
91
0
  NS_WARNING_ASSERTION(builder, "No builder available");
92
0
  return builder.forget();
93
0
}
94
95
void
96
HTMLMenuElement::Build(nsIMenuBuilder* aBuilder)
97
0
{
98
0
  if (!aBuilder) {
99
0
    return;
100
0
  }
101
0
102
0
  BuildSubmenu(EmptyString(), this, aBuilder);
103
0
}
104
105
nsresult
106
HTMLMenuElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
107
                              const nsAttrValue* aValue,
108
                              const nsAttrValue* aOldValue,
109
                              nsIPrincipal* aSubjectPrincipal,
110
                              bool aNotify)
111
0
{
112
0
  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::type) {
113
0
    if (aValue) {
114
0
      mType = aValue->GetEnumValue();
115
0
    } else {
116
0
      mType = kMenuDefaultType->value;
117
0
    }
118
0
  }
119
0
120
0
  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
121
0
                                            aOldValue, aSubjectPrincipal, aNotify);
122
0
}
123
124
bool
125
HTMLMenuElement::ParseAttribute(int32_t aNamespaceID,
126
                                nsAtom* aAttribute,
127
                                const nsAString& aValue,
128
                                nsIPrincipal* aMaybeScriptedPrincipal,
129
                                nsAttrValue& aResult)
130
0
{
131
0
  if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::type) {
132
0
    return aResult.ParseEnumValue(aValue, kMenuTypeTable, false,
133
0
                                  kMenuDefaultType);
134
0
  }
135
0
136
0
  return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
137
0
                                              aMaybeScriptedPrincipal, aResult);
138
0
}
139
140
void
141
HTMLMenuElement::BuildSubmenu(const nsAString& aLabel,
142
                              nsIContent* aContent,
143
                              nsIMenuBuilder* aBuilder)
144
0
{
145
0
  aBuilder->OpenContainer(aLabel);
146
0
147
0
  int8_t separator = ST_TRUE_INIT;
148
0
  TraverseContent(aContent, aBuilder, separator);
149
0
150
0
  if (separator == ST_TRUE) {
151
0
    aBuilder->UndoAddSeparator();
152
0
  }
153
0
154
0
  aBuilder->CloseContainer();
155
0
}
156
157
// static
158
bool
159
HTMLMenuElement::CanLoadIcon(nsIContent* aContent, const nsAString& aIcon)
160
0
{
161
0
  if (aIcon.IsEmpty()) {
162
0
    return false;
163
0
  }
164
0
165
0
  nsIDocument* doc = aContent->OwnerDoc();
166
0
167
0
  nsCOMPtr<nsIURI> baseURI = aContent->GetBaseURI();
168
0
  nsCOMPtr<nsIURI> uri;
169
0
  nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), aIcon, doc,
170
0
                                            baseURI);
171
0
172
0
  if (!uri) {
173
0
    return false;
174
0
  }
175
0
176
0
  return nsContentUtils::CanLoadImage(uri, aContent, doc,
177
0
                                      aContent->NodePrincipal());
178
0
}
179
180
void
181
HTMLMenuElement::TraverseContent(nsIContent* aContent,
182
                                 nsIMenuBuilder* aBuilder,
183
                                 int8_t& aSeparator)
184
0
{
185
0
  nsCOMPtr<nsIContent> child;
186
0
  for (child = aContent->GetFirstChild(); child;
187
0
       child = child->GetNextSibling()) {
188
0
    nsGenericHTMLElement* element = nsGenericHTMLElement::FromNode(child);
189
0
    if (!element) {
190
0
      continue;
191
0
    }
192
0
193
0
    if (child->IsHTMLElement(nsGkAtoms::menuitem)) {
194
0
      HTMLMenuItemElement* menuitem = HTMLMenuItemElement::FromNode(child);
195
0
196
0
      if (menuitem->IsHidden()) {
197
0
        continue;
198
0
      }
199
0
200
0
      nsAutoString label;
201
0
      menuitem->GetLabel(label);
202
0
      if (label.IsEmpty()) {
203
0
        continue;
204
0
      }
205
0
206
0
      nsAutoString icon;
207
0
      menuitem->GetIcon(icon);
208
0
209
0
      aBuilder->AddItemFor(menuitem, CanLoadIcon(child, icon));
210
0
211
0
      aSeparator = ST_FALSE;
212
0
    } else if (child->IsHTMLElement(nsGkAtoms::hr)) {
213
0
      aBuilder->AddSeparator();
214
0
    } else if (child->IsHTMLElement(nsGkAtoms::menu) && !element->IsHidden()) {
215
0
      if (child->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::label)) {
216
0
        nsAutoString label;
217
0
        child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
218
0
219
0
        BuildSubmenu(label, child, aBuilder);
220
0
221
0
        aSeparator = ST_FALSE;
222
0
      } else {
223
0
        AddSeparator(aBuilder, aSeparator);
224
0
225
0
        TraverseContent(child, aBuilder, aSeparator);
226
0
227
0
        AddSeparator(aBuilder, aSeparator);
228
0
      }
229
0
    }
230
0
  }
231
0
}
232
233
inline void
234
HTMLMenuElement::AddSeparator(nsIMenuBuilder* aBuilder, int8_t& aSeparator)
235
0
{
236
0
  if (aSeparator) {
237
0
    return;
238
0
  }
239
0
240
0
  aBuilder->AddSeparator();
241
0
  aSeparator = ST_TRUE;
242
0
}
243
244
JSObject*
245
HTMLMenuElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
246
0
{
247
0
  return HTMLMenuElement_Binding::Wrap(aCx, this, aGivenProto);
248
0
}
249
250
} // namespace dom
251
} // namespace mozilla