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