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/SVGAElement.h"
8 :
9 : #include "mozilla/Attributes.h"
10 : #include "mozilla/EventDispatcher.h"
11 : #include "mozilla/EventStates.h"
12 : #include "mozilla/dom/SVGAElementBinding.h"
13 : #include "nsCOMPtr.h"
14 : #include "nsContentUtils.h"
15 : #include "nsGkAtoms.h"
16 : #include "nsSVGString.h"
17 : #include "nsIURI.h"
18 :
19 0 : NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(A)
20 :
21 : namespace mozilla {
22 : namespace dom {
23 :
24 : JSObject*
25 0 : SVGAElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
26 : {
27 0 : return SVGAElementBinding::Wrap(aCx, this, aGivenProto);
28 : }
29 :
30 : nsSVGElement::StringInfo SVGAElement::sStringInfo[3] =
31 : {
32 : { &nsGkAtoms::href, kNameSpaceID_None, true },
33 : { &nsGkAtoms::href, kNameSpaceID_XLink, true },
34 : { &nsGkAtoms::target, kNameSpaceID_None, true }
35 : };
36 :
37 :
38 : //----------------------------------------------------------------------
39 : // nsISupports methods
40 :
41 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGAElement)
42 0 : NS_INTERFACE_TABLE_INHERITED(SVGAElement,
43 : nsIDOMNode,
44 : nsIDOMElement,
45 : nsIDOMSVGElement,
46 : Link)
47 0 : NS_INTERFACE_TABLE_TAIL_INHERITING(SVGAElementBase)
48 :
49 : NS_IMPL_CYCLE_COLLECTION_CLASS(SVGAElement)
50 :
51 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGAElement,
52 : SVGAElementBase)
53 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
54 :
55 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGAElement,
56 : SVGAElementBase)
57 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
58 :
59 0 : NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase)
60 0 : NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
61 :
62 : //----------------------------------------------------------------------
63 : // Implementation
64 :
65 0 : SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
66 : : SVGAElementBase(aNodeInfo)
67 0 : , Link(this)
68 : {
69 0 : }
70 :
71 0 : SVGAElement::~SVGAElement()
72 : {
73 0 : }
74 :
75 : already_AddRefed<SVGAnimatedString>
76 0 : SVGAElement::Href()
77 : {
78 0 : return mStringAttributes[HREF].IsExplicitlySet()
79 : ? mStringAttributes[HREF].ToDOMAnimatedString(this)
80 0 : : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
81 : }
82 :
83 : //----------------------------------------------------------------------
84 : // Link methods
85 :
86 : bool
87 0 : SVGAElement::ElementHasHref() const
88 : {
89 0 : return mStringAttributes[HREF].IsExplicitlySet() ||
90 0 : mStringAttributes[XLINK_HREF].IsExplicitlySet();
91 : }
92 :
93 : //----------------------------------------------------------------------
94 : // nsINode methods
95 :
96 : nsresult
97 0 : SVGAElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
98 : {
99 0 : nsresult rv = Element::GetEventTargetParent(aVisitor);
100 0 : NS_ENSURE_SUCCESS(rv, rv);
101 :
102 0 : return GetEventTargetParentForLinks(aVisitor);
103 : }
104 :
105 : nsresult
106 0 : SVGAElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
107 : {
108 0 : return PostHandleEventForLinks(aVisitor);
109 : }
110 :
111 0 : NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGAElement)
112 :
113 :
114 : //----------------------------------------------------------------------
115 :
116 : already_AddRefed<SVGAnimatedString>
117 0 : SVGAElement::Target()
118 : {
119 0 : return mStringAttributes[TARGET].ToDOMAnimatedString(this);
120 : }
121 :
122 : void
123 0 : SVGAElement::GetDownload(nsAString & aDownload)
124 : {
125 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::download, aDownload);
126 0 : }
127 :
128 : void
129 0 : SVGAElement::SetDownload(const nsAString & aDownload, ErrorResult& rv)
130 : {
131 0 : rv = SetAttr(kNameSpaceID_None, nsGkAtoms::download, aDownload, true);
132 0 : }
133 :
134 : //----------------------------------------------------------------------
135 : // nsIContent methods
136 :
137 : nsresult
138 0 : SVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
139 : nsIContent *aBindingParent,
140 : bool aCompileEventHandlers)
141 : {
142 0 : Link::ResetLinkState(false, Link::ElementHasHref());
143 :
144 0 : nsresult rv = SVGAElementBase::BindToTree(aDocument, aParent,
145 : aBindingParent,
146 0 : aCompileEventHandlers);
147 0 : NS_ENSURE_SUCCESS(rv, rv);
148 :
149 0 : nsIDocument* doc = GetComposedDoc();
150 0 : if (doc) {
151 0 : doc->RegisterPendingLinkUpdate(this);
152 : }
153 :
154 0 : return NS_OK;
155 : }
156 :
157 : void
158 0 : SVGAElement::UnbindFromTree(bool aDeep, bool aNullParent)
159 : {
160 : // If this link is ever reinserted into a document, it might
161 : // be under a different xml:base, so forget the cached state now.
162 0 : Link::ResetLinkState(false, Link::ElementHasHref());
163 :
164 0 : SVGAElementBase::UnbindFromTree(aDeep, aNullParent);
165 0 : }
166 :
167 : already_AddRefed<nsIURI>
168 0 : SVGAElement::GetHrefURI() const
169 : {
170 0 : nsCOMPtr<nsIURI> hrefURI;
171 0 : return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nullptr;
172 : }
173 :
174 :
175 : NS_IMETHODIMP_(bool)
176 0 : SVGAElement::IsAttributeMapped(const nsIAtom* name) const
177 : {
178 : static const MappedAttributeEntry* const map[] = {
179 : sFEFloodMap,
180 : sFiltersMap,
181 : sFontSpecificationMap,
182 : sGradientStopMap,
183 : sLightingEffectsMap,
184 : sMarkersMap,
185 : sTextContentElementsMap,
186 : sViewportsMap
187 : };
188 :
189 0 : return FindAttributeDependence(name, map) ||
190 0 : SVGAElementBase::IsAttributeMapped(name);
191 : }
192 :
193 : int32_t
194 0 : SVGAElement::TabIndexDefault()
195 : {
196 0 : return 0;
197 : }
198 :
199 : static bool
200 0 : IsNodeInEditableRegion(nsINode* aNode)
201 : {
202 0 : while (aNode) {
203 0 : if (aNode->IsEditable()) {
204 0 : return true;
205 : }
206 0 : aNode = aNode->GetParent();
207 : }
208 0 : return false;
209 : }
210 :
211 : bool
212 0 : SVGAElement::IsSVGFocusable(bool* aIsFocusable, int32_t* aTabIndex)
213 : {
214 0 : if (nsSVGElement::IsSVGFocusable(aIsFocusable, aTabIndex)) {
215 0 : return true;
216 : }
217 :
218 : // cannot focus links if there is no link handler
219 0 : nsIDocument* doc = GetComposedDoc();
220 0 : if (doc) {
221 0 : nsIPresShell* presShell = doc->GetShell();
222 0 : if (presShell) {
223 0 : nsPresContext* presContext = presShell->GetPresContext();
224 0 : if (presContext && !presContext->GetLinkHandler()) {
225 0 : *aIsFocusable = false;
226 0 : return false;
227 : }
228 : }
229 : }
230 :
231 : // Links that are in an editable region should never be focusable, even if
232 : // they are in a contenteditable="false" region.
233 0 : if (IsNodeInEditableRegion(this)) {
234 0 : if (aTabIndex) {
235 0 : *aTabIndex = -1;
236 : }
237 :
238 0 : *aIsFocusable = false;
239 :
240 0 : return true;
241 : }
242 :
243 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
244 : // check whether we're actually a link
245 0 : if (!Link::HasURI()) {
246 : // Not tabbable or focusable without href (bug 17605), unless
247 : // forced to be via presence of nonnegative tabindex attribute
248 0 : if (aTabIndex) {
249 0 : *aTabIndex = -1;
250 : }
251 :
252 0 : *aIsFocusable = false;
253 :
254 0 : return false;
255 : }
256 : }
257 :
258 0 : if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
259 0 : *aTabIndex = -1;
260 : }
261 :
262 0 : *aIsFocusable = true;
263 :
264 0 : return false;
265 : }
266 :
267 : bool
268 0 : SVGAElement::IsLink(nsIURI** aURI) const
269 : {
270 : // To be a clickable XLink for styling and interaction purposes, we require:
271 : //
272 : // xlink:href - must be set
273 : // xlink:type - must be unset or set to "" or set to "simple"
274 : // xlink:show - must be unset or set to "", "new" or "replace"
275 : // xlink:actuate - must be unset or set to "" or "onRequest"
276 : //
277 : // For any other values, we're either not a *clickable* XLink, or the end
278 : // result is poorly specified. Either way, we return false.
279 :
280 : static nsIContent::AttrValuesArray sTypeVals[] =
281 : { &nsGkAtoms::_empty, &nsGkAtoms::simple, nullptr };
282 :
283 : static nsIContent::AttrValuesArray sShowVals[] =
284 : { &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
285 :
286 : static nsIContent::AttrValuesArray sActuateVals[] =
287 : { &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr };
288 :
289 : // Optimization: check for href first for early return
290 0 : bool useBareHref = mStringAttributes[HREF].IsExplicitlySet();
291 :
292 0 : if ((useBareHref || mStringAttributes[XLINK_HREF].IsExplicitlySet()) &&
293 0 : FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
294 0 : sTypeVals, eCaseMatters) !=
295 0 : nsIContent::ATTR_VALUE_NO_MATCH &&
296 0 : FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
297 0 : sShowVals, eCaseMatters) !=
298 0 : nsIContent::ATTR_VALUE_NO_MATCH &&
299 0 : FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate,
300 0 : sActuateVals, eCaseMatters) !=
301 : nsIContent::ATTR_VALUE_NO_MATCH) {
302 0 : nsCOMPtr<nsIURI> baseURI = GetBaseURI();
303 : // Get absolute URI
304 0 : nsAutoString str;
305 0 : const uint8_t idx = useBareHref ? HREF : XLINK_HREF;
306 0 : mStringAttributes[idx].GetAnimValue(str, this);
307 0 : nsContentUtils::NewURIWithDocumentCharset(aURI, str, OwnerDoc(), baseURI);
308 : // must promise out param is non-null if we return true
309 0 : return !!*aURI;
310 : }
311 :
312 0 : *aURI = nullptr;
313 0 : return false;
314 : }
315 :
316 : void
317 0 : SVGAElement::GetLinkTarget(nsAString& aTarget)
318 : {
319 0 : mStringAttributes[TARGET].GetAnimValue(aTarget, this);
320 0 : if (aTarget.IsEmpty()) {
321 :
322 : static nsIContent::AttrValuesArray sShowVals[] =
323 : { &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
324 :
325 0 : switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
326 0 : sShowVals, eCaseMatters)) {
327 : case 0:
328 0 : aTarget.AssignLiteral("_blank");
329 0 : return;
330 : case 1:
331 0 : return;
332 : }
333 0 : nsIDocument* ownerDoc = OwnerDoc();
334 0 : if (ownerDoc) {
335 0 : ownerDoc->GetBaseTarget(aTarget);
336 : }
337 : }
338 : }
339 :
340 : EventStates
341 0 : SVGAElement::IntrinsicState() const
342 : {
343 0 : return Link::LinkState() | SVGAElementBase::IntrinsicState();
344 : }
345 :
346 : nsresult
347 0 : SVGAElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
348 : nsIAtom* aPrefix, const nsAString& aValue,
349 : bool aNotify)
350 : {
351 0 : nsresult rv = SVGAElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
352 0 : aValue, aNotify);
353 :
354 : // The ordering of the parent class's SetAttr call and Link::ResetLinkState
355 : // is important here! The attribute is not set until SetAttr returns, and
356 : // we will need the updated attribute value because notifying the document
357 : // that content states have changed will call IntrinsicState, which will try
358 : // to get updated information about the visitedness from Link.
359 0 : if (aName == nsGkAtoms::href &&
360 0 : (aNameSpaceID == kNameSpaceID_XLink ||
361 : aNameSpaceID == kNameSpaceID_None)) {
362 0 : Link::ResetLinkState(!!aNotify, true);
363 : }
364 :
365 0 : return rv;
366 : }
367 :
368 : nsresult
369 0 : SVGAElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
370 : bool aNotify)
371 : {
372 0 : nsresult rv = nsSVGElement::UnsetAttr(aNameSpaceID, aAttr, aNotify);
373 :
374 : // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
375 : // is important here! The attribute is not unset until UnsetAttr returns, and
376 : // we will need the updated attribute value because notifying the document
377 : // that content states have changed will call IntrinsicState, which will try
378 : // to get updated information about the visitedness from Link.
379 0 : if (aAttr == nsGkAtoms::href &&
380 0 : (aNameSpaceID == kNameSpaceID_XLink ||
381 : aNameSpaceID == kNameSpaceID_None)) {
382 0 : Link::ResetLinkState(!!aNotify, Link::ElementHasHref());
383 : }
384 :
385 0 : return rv;
386 : }
387 :
388 : //----------------------------------------------------------------------
389 : // nsSVGElement methods
390 :
391 : nsSVGElement::StringAttributesInfo
392 0 : SVGAElement::GetStringInfo()
393 : {
394 : return StringAttributesInfo(mStringAttributes, sStringInfo,
395 0 : ArrayLength(sStringInfo));
396 : }
397 :
398 : } // namespace dom
399 : } // namespace mozilla
|