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#if ENABLE(SVG)
26#include "SVGAElement.h"
27
28#include "Attr.h"
29#include "Attribute.h"
30#include "Document.h"
31#include "EventHandler.h"
32#include "EventNames.h"
33#include "Frame.h"
34#include "FrameLoader.h"
35#include "FrameLoaderTypes.h"
36#include "HTMLAnchorElement.h"
37#include "HTMLParserIdioms.h"
38#include "KeyboardEvent.h"
39#include "MouseEvent.h"
40#include "NodeRenderingContext.h"
41#include "PlatformMouseEvent.h"
42#include "RenderSVGInline.h"
43#include "RenderSVGText.h"
44#include "RenderSVGTransformableContainer.h"
45#include "ResourceRequest.h"
46#include "SVGElementInstance.h"
47#include "SVGNames.h"
48#include "SVGSMILElement.h"
49#include "XLinkNames.h"
50
51namespace WebCore {
52
53using namespace HTMLNames;
54
55// Animated property definitions
56DEFINE_ANIMATED_STRING(SVGAElement, SVGNames::targetAttr, SVGTarget, svgTarget)
57DEFINE_ANIMATED_STRING(SVGAElement, XLinkNames::hrefAttr, Href, href)
58DEFINE_ANIMATED_BOOLEAN(SVGAElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
59
60BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGAElement)
61     REGISTER_LOCAL_ANIMATED_PROPERTY(svgTarget)
62     REGISTER_LOCAL_ANIMATED_PROPERTY(href)
63     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
64     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledTransformableElement)
65     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
66END_REGISTER_ANIMATED_PROPERTIES
67
68inline SVGAElement::SVGAElement(const QualifiedName& tagName, Document* document)
69    : SVGStyledTransformableElement(tagName, document)
70{
71    ASSERT(hasTagName(SVGNames::aTag));
72    registerAnimatedPropertiesForSVGAElement();
73}
74
75PassRefPtr<SVGAElement> SVGAElement::create(const QualifiedName& tagName, Document* document)
76{
77    return adoptRef(new SVGAElement(tagName, document));
78}
79
80String SVGAElement::title() const
81{
82    // If the xlink:title is set (non-empty string), use it.
83    const AtomicString& title = fastGetAttribute(XLinkNames::titleAttr);
84    if (!title.isEmpty())
85        return title;
86
87    // Otherwise, use the title of this element.
88    return SVGStyledElement::title();
89}
90
91bool SVGAElement::isSupportedAttribute(const QualifiedName& attrName)
92{
93    DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
94    if (supportedAttributes.isEmpty()) {
95        SVGURIReference::addSupportedAttributes(supportedAttributes);
96        SVGTests::addSupportedAttributes(supportedAttributes);
97        SVGLangSpace::addSupportedAttributes(supportedAttributes);
98        SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
99        supportedAttributes.add(SVGNames::targetAttr);
100    }
101    return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
102}
103
104void SVGAElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
105{
106    if (!isSupportedAttribute(name)) {
107        SVGStyledTransformableElement::parseAttribute(name, value);
108        return;
109    }
110
111    if (name == SVGNames::targetAttr) {
112        setSVGTargetBaseValue(value);
113        return;
114    }
115
116    if (SVGURIReference::parseAttribute(name, value))
117        return;
118    if (SVGTests::parseAttribute(name, value))
119        return;
120    if (SVGLangSpace::parseAttribute(name, value))
121        return;
122    if (SVGExternalResourcesRequired::parseAttribute(name, value))
123        return;
124
125    ASSERT_NOT_REACHED();
126}
127
128void SVGAElement::svgAttributeChanged(const QualifiedName& attrName)
129{
130    if (!isSupportedAttribute(attrName)) {
131        SVGStyledTransformableElement::svgAttributeChanged(attrName);
132        return;
133    }
134
135    SVGElementInstance::InvalidationGuard invalidationGuard(this);
136
137    // Unlike other SVG*Element classes, SVGAElement only listens to SVGURIReference changes
138    // as none of the other properties changes the linking behaviour for our <a> element.
139    if (SVGURIReference::isKnownAttribute(attrName)) {
140        bool wasLink = isLink();
141        setIsLink(!href().isNull());
142
143        if (wasLink != isLink())
144            setNeedsStyleRecalc();
145    }
146}
147
148RenderObject* SVGAElement::createRenderer(RenderArena* arena, RenderStyle*)
149{
150    if (parentNode() && parentNode()->isSVGElement() && toSVGElement(parentNode())->isTextContent())
151        return new (arena) RenderSVGInline(this);
152
153    return new (arena) RenderSVGTransformableContainer(this);
154}
155
156void SVGAElement::defaultEventHandler(Event* event)
157{
158    if (isLink()) {
159        if (focused() && isEnterKeyKeydownEvent(event)) {
160            event->setDefaultHandled();
161            dispatchSimulatedClick(event);
162            return;
163        }
164
165        if (isLinkClick(event)) {
166            String url = stripLeadingAndTrailingHTMLSpaces(href());
167
168            if (url[0] == '#') {
169                Element* targetElement = treeScope()->getElementById(url.substring(1));
170                if (SVGSMILElement::isSMILElement(targetElement)) {
171                    static_cast<SVGSMILElement*>(targetElement)->beginByLinkActivation();
172                    event->setDefaultHandled();
173                    return;
174                }
175                // Only allow navigation to internal <view> anchors.
176                if (targetElement && !targetElement->hasTagName(SVGNames::viewTag))
177                    return;
178            }
179
180            String target = this->target();
181            if (target.isEmpty() && fastGetAttribute(XLinkNames::showAttr) == "new")
182                target = "_blank";
183            event->setDefaultHandled();
184
185            Frame* frame = document()->frame();
186            if (!frame)
187                return;
188            frame->loader()->urlSelected(document()->completeURL(url), target, event, false, false, MaybeSendReferrer);
189            return;
190        }
191    }
192
193    SVGStyledTransformableElement::defaultEventHandler(event);
194}
195
196bool SVGAElement::supportsFocus() const
197{
198    if (rendererIsEditable())
199        return SVGStyledTransformableElement::supportsFocus();
200    return true;
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 || SVGStyledTransformableElement::isURLAttribute(attribute);
214}
215
216bool SVGAElement::isMouseFocusable() const
217{
218    return false;
219}
220
221bool SVGAElement::isKeyboardFocusable(KeyboardEvent* event) const
222{
223    if (!isFocusable())
224        return false;
225
226    if (!document()->frame())
227        return false;
228
229    return document()->frame()->eventHandler()->tabsToLinks(event);
230}
231
232bool SVGAElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
233{
234    // http://www.w3.org/2003/01/REC-SVG11-20030114-errata#linking-text-environment
235    // The 'a' element may contain any element that its parent may contain, except itself.
236    if (childContext.node()->hasTagName(SVGNames::aTag))
237        return false;
238    if (parentNode() && parentNode()->isSVGElement())
239        return parentNode()->childShouldCreateRenderer(childContext);
240
241    return SVGElement::childShouldCreateRenderer(childContext);
242}
243
244} // namespace WebCore
245
246#endif // ENABLE(SVG)
247