Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2007 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2007 Eric Seidel <eric (at) 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 
     52 namespace WebCore {
     53 
     54 using namespace HTMLNames;
     55 
     56 inline SVGAElement::SVGAElement(Document& document)
     57     : SVGGraphicsElement(SVGNames::aTag, document)
     58     , SVGURIReference(this)
     59     , m_svgTarget(SVGAnimatedString::create(this, SVGNames::targetAttr, SVGString::create()))
     60 {
     61     ScriptWrappable::init(this);
     62     addToPropertyMap(m_svgTarget);
     63 }
     64 
     65 DEFINE_NODE_FACTORY(SVGAElement)
     66 
     67 String 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 
     78 bool SVGAElement::isSupportedAttribute(const QualifiedName& attrName)
     79 {
     80     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
     81     if (supportedAttributes.isEmpty()) {
     82         SVGURIReference::addSupportedAttributes(supportedAttributes);
     83         supportedAttributes.add(SVGNames::targetAttr);
     84     }
     85     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
     86 }
     87 
     88 void SVGAElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
     89 {
     90     if (!isSupportedAttribute(name)) {
     91         SVGGraphicsElement::parseAttribute(name, value);
     92         return;
     93     }
     94 
     95     SVGParsingError parseError = NoError;
     96 
     97     if (name == SVGNames::targetAttr) {
     98         m_svgTarget->setBaseValueAsString(value, parseError);
     99     } else if (SVGURIReference::parseAttribute(name, value, parseError)) {
    100     } else {
    101         ASSERT_NOT_REACHED();
    102     }
    103 
    104     reportAttributeParsingError(parseError, name, value);
    105 }
    106 
    107 void SVGAElement::svgAttributeChanged(const QualifiedName& attrName)
    108 {
    109     if (!isSupportedAttribute(attrName)) {
    110         SVGGraphicsElement::svgAttributeChanged(attrName);
    111         return;
    112     }
    113 
    114     SVGElement::InvalidationGuard invalidationGuard(this);
    115 
    116     // Unlike other SVG*Element classes, SVGAElement only listens to SVGURIReference changes
    117     // as none of the other properties changes the linking behaviour for our <a> element.
    118     if (SVGURIReference::isKnownAttribute(attrName)) {
    119         bool wasLink = isLink();
    120         setIsLink(!hrefString().isNull());
    121 
    122         if (wasLink != isLink())
    123             setNeedsStyleRecalc(SubtreeStyleChange);
    124     }
    125 }
    126 
    127 RenderObject* SVGAElement::createRenderer(RenderStyle*)
    128 {
    129     if (parentNode() && parentNode()->isSVGElement() && toSVGElement(parentNode())->isTextContent())
    130         return new RenderSVGInline(this);
    131 
    132     return new RenderSVGTransformableContainer(this);
    133 }
    134 
    135 void SVGAElement::defaultEventHandler(Event* event)
    136 {
    137     if (isLink()) {
    138         if (focused() && isEnterKeyKeydownEvent(event)) {
    139             event->setDefaultHandled();
    140             dispatchSimulatedClick(event);
    141             return;
    142         }
    143 
    144         if (isLinkClick(event)) {
    145             String url = stripLeadingAndTrailingHTMLSpaces(hrefString());
    146 
    147             if (url[0] == '#') {
    148                 Element* targetElement = treeScope().getElementById(AtomicString(url.substring(1)));
    149                 if (targetElement && isSVGSMILElement(*targetElement)) {
    150                     toSVGSMILElement(targetElement)->beginByLinkActivation();
    151                     event->setDefaultHandled();
    152                     return;
    153                 }
    154             }
    155 
    156             AtomicString target(m_svgTarget->currentValue()->value());
    157             if (target.isEmpty() && fastGetAttribute(XLinkNames::showAttr) == "new")
    158                 target = AtomicString("_blank", AtomicString::ConstructFromLiteral);
    159             event->setDefaultHandled();
    160 
    161             LocalFrame* frame = document().frame();
    162             if (!frame)
    163                 return;
    164             FrameLoadRequest frameRequest(&document(), ResourceRequest(document().completeURL(url)), target);
    165             frameRequest.setTriggeringEvent(event);
    166             frame->loader().load(frameRequest);
    167             return;
    168         }
    169     }
    170 
    171     SVGGraphicsElement::defaultEventHandler(event);
    172 }
    173 
    174 short SVGAElement::tabIndex() const
    175 {
    176     // Skip the supportsFocus check in SVGElement.
    177     return Element::tabIndex();
    178 }
    179 
    180 bool SVGAElement::supportsFocus() const
    181 {
    182     if (rendererIsEditable())
    183         return SVGGraphicsElement::supportsFocus();
    184     // If not a link we should still be able to focus the element if it has tabIndex.
    185     return isLink() || Element::supportsFocus();
    186 }
    187 
    188 bool SVGAElement::isURLAttribute(const Attribute& attribute) const
    189 {
    190     return attribute.name().localName() == hrefAttr || SVGGraphicsElement::isURLAttribute(attribute);
    191 }
    192 
    193 bool SVGAElement::isMouseFocusable() const
    194 {
    195     // Links are focusable by default, but only allow links with tabindex or contenteditable to be mouse focusable.
    196     // https://bugs.webkit.org/show_bug.cgi?id=26856
    197     if (isLink())
    198         return Element::supportsFocus();
    199 
    200     return SVGElement::isMouseFocusable();
    201 }
    202 
    203 bool SVGAElement::isKeyboardFocusable() const
    204 {
    205     if (isFocusable() && Element::supportsFocus())
    206         return SVGElement::isKeyboardFocusable();
    207 
    208     if (isLink())
    209         return document().frameHost()->chrome().client().tabsToLinks();
    210     return SVGElement::isKeyboardFocusable();
    211 }
    212 
    213 bool SVGAElement::canStartSelection() const
    214 {
    215     if (!isLink())
    216         return SVGElement::canStartSelection();
    217     return rendererIsEditable();
    218 }
    219 
    220 bool SVGAElement::willRespondToMouseClickEvents()
    221 {
    222     return isLink() || SVGGraphicsElement::willRespondToMouseClickEvents();
    223 }
    224 
    225 } // namespace WebCore
    226