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 "core/SVGNames.h"
28#include "core/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/frame/FrameHost.h"
35#include "core/frame/LocalFrame.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/animation/SVGSMILElement.h"
49#include "platform/PlatformMouseEvent.h"
50#include "platform/network/ResourceRequest.h"
51
52namespace blink {
53
54using namespace HTMLNames;
55
56inline SVGAElement::SVGAElement(Document& document)
57    : SVGGraphicsElement(SVGNames::aTag, document)
58    , SVGURIReference(this)
59    , m_svgTarget(SVGAnimatedString::create(this, SVGNames::targetAttr, SVGString::create()))
60    , m_wasFocusedByMouse(false)
61{
62    addToPropertyMap(m_svgTarget);
63}
64
65DEFINE_NODE_FACTORY(SVGAElement)
66
67String SVGAElement::title() const
68{
69    // If the xlink:title is set (non-empty string), use it.
70    const AtomicString& title = fastGetAttribute(XLinkNames::titleAttr);
71    if (!title.isEmpty())
72        return title;
73
74    // Otherwise, use the title of this element.
75    return SVGElement::title();
76}
77
78void SVGAElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
79{
80    parseAttributeNew(name, value);
81}
82
83void SVGAElement::svgAttributeChanged(const QualifiedName& attrName)
84{
85    // Unlike other SVG*Element classes, SVGAElement only listens to SVGURIReference changes
86    // as none of the other properties changes the linking behaviour for our <a> element.
87    if (SVGURIReference::isKnownAttribute(attrName)) {
88        SVGElement::InvalidationGuard invalidationGuard(this);
89
90        bool wasLink = isLink();
91        setIsLink(!hrefString().isNull());
92
93        if (wasLink != isLink())
94            setNeedsStyleRecalc(SubtreeStyleChange);
95
96        return;
97    }
98
99    SVGGraphicsElement::svgAttributeChanged(attrName);
100}
101
102RenderObject* SVGAElement::createRenderer(RenderStyle*)
103{
104    if (parentNode() && parentNode()->isSVGElement() && toSVGElement(parentNode())->isTextContent())
105        return new RenderSVGInline(this);
106
107    return new RenderSVGTransformableContainer(this);
108}
109
110void SVGAElement::defaultEventHandler(Event* event)
111{
112    if (isLink()) {
113        if (focused() && isEnterKeyKeydownEvent(event)) {
114            event->setDefaultHandled();
115            dispatchSimulatedClick(event);
116            return;
117        }
118
119        if (isLinkClick(event)) {
120            String url = stripLeadingAndTrailingHTMLSpaces(hrefString());
121
122            if (url[0] == '#') {
123                Element* targetElement = treeScope().getElementById(AtomicString(url.substring(1)));
124                if (targetElement && isSVGSMILElement(*targetElement)) {
125                    toSVGSMILElement(targetElement)->beginByLinkActivation();
126                    event->setDefaultHandled();
127                    return;
128                }
129            }
130
131            AtomicString target(m_svgTarget->currentValue()->value());
132            if (target.isEmpty() && fastGetAttribute(XLinkNames::showAttr) == "new")
133                target = AtomicString("_blank", AtomicString::ConstructFromLiteral);
134            event->setDefaultHandled();
135
136            LocalFrame* frame = document().frame();
137            if (!frame)
138                return;
139            FrameLoadRequest frameRequest(&document(), ResourceRequest(document().completeURL(url)), target);
140            frameRequest.setTriggeringEvent(event);
141            frame->loader().load(frameRequest);
142            return;
143        }
144    }
145
146    SVGGraphicsElement::defaultEventHandler(event);
147}
148
149short SVGAElement::tabIndex() const
150{
151    // Skip the supportsFocus check in SVGElement.
152    return Element::tabIndex();
153}
154
155bool SVGAElement::supportsFocus() const
156{
157    if (hasEditableStyle())
158        return SVGGraphicsElement::supportsFocus();
159    // If not a link we should still be able to focus the element if it has tabIndex.
160    return isLink() || SVGGraphicsElement::supportsFocus();
161}
162
163bool SVGAElement::shouldHaveFocusAppearance() const
164{
165    return !m_wasFocusedByMouse || SVGGraphicsElement::supportsFocus();
166}
167
168void SVGAElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type)
169{
170    if (type != FocusTypePage)
171        m_wasFocusedByMouse = type == FocusTypeMouse;
172    SVGGraphicsElement::dispatchFocusEvent(oldFocusedElement, type);
173}
174
175bool SVGAElement::isURLAttribute(const Attribute& attribute) const
176{
177    return attribute.name().localName() == hrefAttr || SVGGraphicsElement::isURLAttribute(attribute);
178}
179
180bool SVGAElement::isMouseFocusable() const
181{
182    if (isLink())
183        return supportsFocus();
184
185    return SVGElement::isMouseFocusable();
186}
187
188bool SVGAElement::isKeyboardFocusable() const
189{
190    if (isFocusable() && Element::supportsFocus())
191        return SVGElement::isKeyboardFocusable();
192
193    if (isLink())
194        return document().frameHost()->chrome().client().tabsToLinks();
195    return SVGElement::isKeyboardFocusable();
196}
197
198bool SVGAElement::canStartSelection() const
199{
200    if (!isLink())
201        return SVGElement::canStartSelection();
202    return hasEditableStyle();
203}
204
205bool SVGAElement::willRespondToMouseClickEvents()
206{
207    return isLink() || SVGGraphicsElement::willRespondToMouseClickEvents();
208}
209
210} // namespace blink
211