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
53namespace WebCore {
54
55using namespace HTMLNames;
56
57// Animated property definitions
58DEFINE_ANIMATED_STRING(SVGAElement, SVGNames::targetAttr, SVGTarget, svgTarget)
59DEFINE_ANIMATED_STRING(SVGAElement, XLinkNames::hrefAttr, Href, href)
60DEFINE_ANIMATED_BOOLEAN(SVGAElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
61
62BEGIN_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)
67END_REGISTER_ANIMATED_PROPERTIES
68
69inline SVGAElement::SVGAElement(Document& document)
70    : SVGGraphicsElement(SVGNames::aTag, document)
71{
72    ScriptWrappable::init(this);
73    registerAnimatedPropertiesForSVGAElement();
74}
75
76PassRefPtr<SVGAElement> SVGAElement::create(Document& document)
77{
78    return adoptRef(new SVGAElement(document));
79}
80
81String 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
92bool 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
103void 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
123void 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
143RenderObject* 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
151void 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
193bool SVGAElement::supportsFocus() const
194{
195    if (rendererIsEditable())
196        return SVGGraphicsElement::supportsFocus();
197    return true;
198}
199
200bool SVGAElement::rendererIsFocusable() const
201{
202    if (renderer() && renderer()->absoluteClippedOverflowRect().isEmpty())
203        return false;
204
205    return SVGElement::rendererIsFocusable();
206}
207
208bool SVGAElement::isURLAttribute(const Attribute& attribute) const
209{
210    return attribute.name().localName() == hrefAttr || SVGGraphicsElement::isURLAttribute(attribute);
211}
212
213bool SVGAElement::isMouseFocusable() const
214{
215    return false;
216}
217
218bool 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
228bool 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