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#include "SVGAElement.h"
25
26#include "Attr.h"
27#include "Attribute.h"
28#include "Document.h"
29#include "EventHandler.h"
30#include "EventNames.h"
31#include "Frame.h"
32#include "FrameLoader.h"
33#include "FrameLoaderTypes.h"
34#include "HTMLAnchorElement.h"
35#include "HTMLParserIdioms.h"
36#include "KeyboardEvent.h"
37#include "MouseEvent.h"
38#include "PlatformMouseEvent.h"
39#include "RenderSVGInline.h"
40#include "RenderSVGText.h"
41#include "RenderSVGTransformableContainer.h"
42#include "ResourceRequest.h"
43#include "SVGElementInstance.h"
44#include "SVGNames.h"
45#include "SVGSMILElement.h"
46#include "XLinkNames.h"
47#include <wtf/NeverDestroyed.h>
48
49namespace WebCore {
50
51using namespace HTMLNames;
52
53// Animated property definitions
54DEFINE_ANIMATED_STRING(SVGAElement, SVGNames::targetAttr, SVGTarget, svgTarget)
55DEFINE_ANIMATED_STRING(SVGAElement, XLinkNames::hrefAttr, Href, href)
56DEFINE_ANIMATED_BOOLEAN(SVGAElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
57
58BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGAElement)
59    REGISTER_LOCAL_ANIMATED_PROPERTY(svgTarget)
60    REGISTER_LOCAL_ANIMATED_PROPERTY(href)
61    REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
62    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
63END_REGISTER_ANIMATED_PROPERTIES
64
65inline SVGAElement::SVGAElement(const QualifiedName& tagName, Document& document)
66    : SVGGraphicsElement(tagName, document)
67{
68    ASSERT(hasTagName(SVGNames::aTag));
69    registerAnimatedPropertiesForSVGAElement();
70}
71
72PassRefPtr<SVGAElement> SVGAElement::create(const QualifiedName& tagName, Document& document)
73{
74    return adoptRef(new SVGAElement(tagName, document));
75}
76
77String SVGAElement::title() const
78{
79    // If the xlink:title is set (non-empty string), use it.
80    const AtomicString& title = fastGetAttribute(XLinkNames::titleAttr);
81    if (!title.isEmpty())
82        return title;
83
84    // Otherwise, use the title of this element.
85    return SVGElement::title();
86}
87
88bool SVGAElement::isSupportedAttribute(const QualifiedName& attrName)
89{
90    static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes;
91    if (supportedAttributes.get().isEmpty()) {
92        SVGURIReference::addSupportedAttributes(supportedAttributes);
93        SVGLangSpace::addSupportedAttributes(supportedAttributes);
94        SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
95        supportedAttributes.get().add(SVGNames::targetAttr);
96    }
97    return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
98}
99
100void SVGAElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
101{
102    if (!isSupportedAttribute(name)) {
103        SVGGraphicsElement::parseAttribute(name, value);
104        return;
105    }
106
107    if (name == SVGNames::targetAttr) {
108        setSVGTargetBaseValue(value);
109        return;
110    }
111
112    if (SVGURIReference::parseAttribute(name, value))
113        return;
114    if (SVGLangSpace::parseAttribute(name, value))
115        return;
116    if (SVGExternalResourcesRequired::parseAttribute(name, value))
117        return;
118
119    ASSERT_NOT_REACHED();
120}
121
122void SVGAElement::svgAttributeChanged(const QualifiedName& attrName)
123{
124    if (!isSupportedAttribute(attrName)) {
125        SVGGraphicsElement::svgAttributeChanged(attrName);
126        return;
127    }
128
129    SVGElementInstance::InvalidationGuard invalidationGuard(this);
130
131    // Unlike other SVG*Element classes, SVGAElement only listens to SVGURIReference changes
132    // as none of the other properties changes the linking behaviour for our <a> element.
133    if (SVGURIReference::isKnownAttribute(attrName)) {
134        bool wasLink = isLink();
135        setIsLink(!href().isNull() && !shouldProhibitLinks(this));
136        if (wasLink != isLink())
137            setNeedsStyleRecalc();
138    }
139}
140
141RenderPtr<RenderElement> SVGAElement::createElementRenderer(PassRef<RenderStyle> style)
142{
143    if (parentNode() && parentNode()->isSVGElement() && toSVGElement(parentNode())->isTextContent())
144        return createRenderer<RenderSVGInline>(*this, WTF::move(style));
145
146    return createRenderer<RenderSVGTransformableContainer>(*this, WTF::move(style));
147}
148
149void SVGAElement::defaultEventHandler(Event* event)
150{
151    if (isLink()) {
152        if (focused() && isEnterKeyKeydownEvent(event)) {
153            event->setDefaultHandled();
154            dispatchSimulatedClick(event);
155            return;
156        }
157
158        if (isLinkClick(event)) {
159            String url = stripLeadingAndTrailingHTMLSpaces(href());
160
161            if (url[0] == '#') {
162                Element* targetElement = treeScope().getElementById(url.substringSharingImpl(1));
163                if (targetElement && isSVGSMILElement(*targetElement)) {
164                    toSVGSMILElement(*targetElement).beginByLinkActivation();
165                    event->setDefaultHandled();
166                    return;
167                }
168                // Only allow navigation to internal <view> anchors.
169                if (targetElement && !targetElement->hasTagName(SVGNames::viewTag))
170                    return;
171            }
172
173            String target = this->target();
174            if (target.isEmpty() && fastGetAttribute(XLinkNames::showAttr) == "new")
175                target = "_blank";
176            event->setDefaultHandled();
177
178            Frame* frame = document().frame();
179            if (!frame)
180                return;
181            frame->loader().urlSelected(document().completeURL(url), target, event, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer);
182            return;
183        }
184    }
185
186    SVGGraphicsElement::defaultEventHandler(event);
187}
188
189short SVGAElement::tabIndex() const
190{
191    // Skip the supportsFocus check in SVGElement.
192    return Element::tabIndex();
193}
194
195bool SVGAElement::supportsFocus() const
196{
197    if (hasEditableStyle())
198        return SVGGraphicsElement::supportsFocus();
199    // If not a link we should still be able to focus the element if it has a tabIndex.
200    return isLink() || Element::supportsFocus();
201}
202
203bool SVGAElement::isFocusable() const
204{
205    if (renderer() && renderer()->absoluteClippedOverflowRect().isEmpty())
206        return false;
207
208    return SVGElement::isFocusable();
209}
210
211bool SVGAElement::isURLAttribute(const Attribute& attribute) const
212{
213    return attribute.name().localName() == hrefAttr || SVGGraphicsElement::isURLAttribute(attribute);
214}
215
216bool SVGAElement::isMouseFocusable() const
217{
218    // Links are focusable by default, but only allow links with tabindex or contenteditable to be mouse focusable.
219    // https://bugs.webkit.org/show_bug.cgi?id=26856
220    if (isLink())
221        return Element::supportsFocus();
222
223    return SVGElement::isMouseFocusable();
224}
225
226bool SVGAElement::isKeyboardFocusable(KeyboardEvent* event) const
227{
228    if (isFocusable() && Element::supportsFocus())
229        return SVGElement::isKeyboardFocusable(event);
230
231    if (isLink())
232        return document().frame()->eventHandler().tabsToLinks(event);
233
234    return SVGElement::isKeyboardFocusable(event);
235}
236
237bool SVGAElement::canStartSelection() const
238{
239    if (!isLink())
240        return SVGElement::canStartSelection();
241
242    return hasEditableStyle();
243}
244
245bool SVGAElement::childShouldCreateRenderer(const Node& child) const
246{
247    // http://www.w3.org/2003/01/REC-SVG11-20030114-errata#linking-text-environment
248    // The 'a' element may contain any element that its parent may contain, except itself.
249    if (child.hasTagName(SVGNames::aTag))
250        return false;
251    if (parentNode() && parentNode()->isSVGElement())
252        return parentNode()->childShouldCreateRenderer(child);
253
254    return SVGElement::childShouldCreateRenderer(child);
255}
256
257bool SVGAElement::willRespondToMouseClickEvents()
258{
259    return isLink() || SVGGraphicsElement::willRespondToMouseClickEvents();
260}
261
262} // namespace WebCore
263