1 /*
2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2010 Apple Inc. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24
25 #include "core/svg/SVGAElement.h"
26
27 #include "SVGNames.h"
28 #include "XLinkNames.h"
29 #include "core/dom/Attr.h"
30 #include "core/dom/Attribute.h"
31 #include "core/dom/Document.h"
32 #include "core/events/KeyboardEvent.h"
33 #include "core/events/MouseEvent.h"
34 #include "core/events/ThreadLocalEventNames.h"
35 #include "core/frame/Frame.h"
36 #include "core/html/HTMLAnchorElement.h"
37 #include "core/html/HTMLFormElement.h"
38 #include "core/html/parser/HTMLParserIdioms.h"
39 #include "core/loader/FrameLoadRequest.h"
40 #include "core/loader/FrameLoader.h"
41 #include "core/loader/FrameLoaderTypes.h"
42 #include "core/page/Chrome.h"
43 #include "core/page/ChromeClient.h"
44 #include "core/page/Page.h"
45 #include "core/rendering/svg/RenderSVGInline.h"
46 #include "core/rendering/svg/RenderSVGText.h"
47 #include "core/rendering/svg/RenderSVGTransformableContainer.h"
48 #include "core/svg/SVGElementInstance.h"
49 #include "core/svg/animation/SVGSMILElement.h"
50 #include "platform/PlatformMouseEvent.h"
51 #include "platform/network/ResourceRequest.h"
52
53 namespace WebCore {
54
55 using namespace HTMLNames;
56
57 // Animated property definitions
DEFINE_ANIMATED_STRING(SVGAElement,SVGNames::targetAttr,SVGTarget,svgTarget)58 DEFINE_ANIMATED_STRING(SVGAElement, SVGNames::targetAttr, SVGTarget, svgTarget)
59 DEFINE_ANIMATED_STRING(SVGAElement, XLinkNames::hrefAttr, Href, href)
60 DEFINE_ANIMATED_BOOLEAN(SVGAElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
61
62 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGAElement)
63 REGISTER_LOCAL_ANIMATED_PROPERTY(svgTarget)
64 REGISTER_LOCAL_ANIMATED_PROPERTY(href)
65 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
66 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
67 END_REGISTER_ANIMATED_PROPERTIES
68
69 inline SVGAElement::SVGAElement(Document& document)
70 : SVGGraphicsElement(SVGNames::aTag, document)
71 {
72 ScriptWrappable::init(this);
73 registerAnimatedPropertiesForSVGAElement();
74 }
75
create(Document & document)76 PassRefPtr<SVGAElement> SVGAElement::create(Document& document)
77 {
78 return adoptRef(new SVGAElement(document));
79 }
80
title() const81 String SVGAElement::title() const
82 {
83 // If the xlink:title is set (non-empty string), use it.
84 const AtomicString& title = fastGetAttribute(XLinkNames::titleAttr);
85 if (!title.isEmpty())
86 return title;
87
88 // Otherwise, use the title of this element.
89 return SVGElement::title();
90 }
91
isSupportedAttribute(const QualifiedName & attrName)92 bool SVGAElement::isSupportedAttribute(const QualifiedName& attrName)
93 {
94 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
95 if (supportedAttributes.isEmpty()) {
96 SVGURIReference::addSupportedAttributes(supportedAttributes);
97 SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
98 supportedAttributes.add(SVGNames::targetAttr);
99 }
100 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
101 }
102
parseAttribute(const QualifiedName & name,const AtomicString & value)103 void SVGAElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
104 {
105 if (!isSupportedAttribute(name)) {
106 SVGGraphicsElement::parseAttribute(name, value);
107 return;
108 }
109
110 if (name == SVGNames::targetAttr) {
111 setSVGTargetBaseValue(value);
112 return;
113 }
114
115 if (SVGURIReference::parseAttribute(name, value))
116 return;
117 if (SVGExternalResourcesRequired::parseAttribute(name, value))
118 return;
119
120 ASSERT_NOT_REACHED();
121 }
122
svgAttributeChanged(const QualifiedName & attrName)123 void SVGAElement::svgAttributeChanged(const QualifiedName& attrName)
124 {
125 if (!isSupportedAttribute(attrName)) {
126 SVGGraphicsElement::svgAttributeChanged(attrName);
127 return;
128 }
129
130 SVGElementInstance::InvalidationGuard invalidationGuard(this);
131
132 // Unlike other SVG*Element classes, SVGAElement only listens to SVGURIReference changes
133 // as none of the other properties changes the linking behaviour for our <a> element.
134 if (SVGURIReference::isKnownAttribute(attrName)) {
135 bool wasLink = isLink();
136 setIsLink(!hrefCurrentValue().isNull());
137
138 if (wasLink != isLink())
139 setNeedsStyleRecalc();
140 }
141 }
142
createRenderer(RenderStyle *)143 RenderObject* SVGAElement::createRenderer(RenderStyle*)
144 {
145 if (parentNode() && parentNode()->isSVGElement() && toSVGElement(parentNode())->isTextContent())
146 return new RenderSVGInline(this);
147
148 return new RenderSVGTransformableContainer(this);
149 }
150
defaultEventHandler(Event * event)151 void SVGAElement::defaultEventHandler(Event* event)
152 {
153 if (isLink()) {
154 if (focused() && isEnterKeyKeydownEvent(event)) {
155 event->setDefaultHandled();
156 dispatchSimulatedClick(event);
157 return;
158 }
159
160 if (isLinkClick(event)) {
161 String url = stripLeadingAndTrailingHTMLSpaces(hrefCurrentValue());
162
163 if (url[0] == '#') {
164 Element* targetElement = treeScope().getElementById(url.substring(1));
165 if (targetElement && isSVGSMILElement(*targetElement)) {
166 toSVGSMILElement(targetElement)->beginByLinkActivation();
167 event->setDefaultHandled();
168 return;
169 }
170 // Only allow navigation to internal <view> anchors.
171 if (targetElement && !targetElement->hasTagName(SVGNames::viewTag))
172 return;
173 }
174
175 String target = this->target();
176 if (target.isEmpty() && fastGetAttribute(XLinkNames::showAttr) == "new")
177 target = "_blank";
178 event->setDefaultHandled();
179
180 Frame* frame = document().frame();
181 if (!frame)
182 return;
183 FrameLoadRequest frameRequest(&document(), ResourceRequest(document().completeURL(url)), target);
184 frameRequest.setTriggeringEvent(event);
185 frame->loader().load(frameRequest);
186 return;
187 }
188 }
189
190 SVGGraphicsElement::defaultEventHandler(event);
191 }
192
supportsFocus() const193 bool SVGAElement::supportsFocus() const
194 {
195 if (rendererIsEditable())
196 return SVGGraphicsElement::supportsFocus();
197 return true;
198 }
199
rendererIsFocusable() const200 bool SVGAElement::rendererIsFocusable() const
201 {
202 if (renderer() && renderer()->absoluteClippedOverflowRect().isEmpty())
203 return false;
204
205 return SVGElement::rendererIsFocusable();
206 }
207
isURLAttribute(const Attribute & attribute) const208 bool SVGAElement::isURLAttribute(const Attribute& attribute) const
209 {
210 return attribute.name().localName() == hrefAttr || SVGGraphicsElement::isURLAttribute(attribute);
211 }
212
isMouseFocusable() const213 bool SVGAElement::isMouseFocusable() const
214 {
215 return false;
216 }
217
isKeyboardFocusable() const218 bool SVGAElement::isKeyboardFocusable() const
219 {
220 if (!isFocusable())
221 return false;
222
223 if (Page* page = document().page())
224 return page->chrome().client().tabsToLinks();
225 return false;
226 }
227
childShouldCreateRenderer(const Node & child) const228 bool SVGAElement::childShouldCreateRenderer(const Node& child) const
229 {
230 // http://www.w3.org/2003/01/REC-SVG11-20030114-errata#linking-text-environment
231 // The 'a' element may contain any element that its parent may contain, except itself.
232 if (child.hasTagName(SVGNames::aTag))
233 return false;
234 if (parentNode() && parentNode()->isSVGElement())
235 return parentNode()->childShouldCreateRenderer(child);
236
237 return SVGGraphicsElement::childShouldCreateRenderer(child);
238 }
239
240 } // namespace WebCore
241