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 "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/dom/EventNames.h" 33 #include "core/dom/KeyboardEvent.h" 34 #include "core/dom/MouseEvent.h" 35 #include "core/dom/NodeRenderingContext.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/Frame.h" 45 #include "core/page/Page.h" 46 #include "core/platform/PlatformMouseEvent.h" 47 #include "core/platform/network/ResourceRequest.h" 48 #include "core/rendering/svg/RenderSVGInline.h" 49 #include "core/rendering/svg/RenderSVGText.h" 50 #include "core/rendering/svg/RenderSVGTransformableContainer.h" 51 #include "core/svg/SVGElementInstance.h" 52 #include "core/svg/animation/SVGSMILElement.h" 53 54 namespace WebCore { 55 56 using namespace HTMLNames; 57 58 // Animated property definitions 59 DEFINE_ANIMATED_STRING(SVGAElement, SVGNames::targetAttr, SVGTarget, svgTarget) 60 DEFINE_ANIMATED_STRING(SVGAElement, XLinkNames::hrefAttr, Href, href) 61 DEFINE_ANIMATED_BOOLEAN(SVGAElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 62 63 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGAElement) 64 REGISTER_LOCAL_ANIMATED_PROPERTY(svgTarget) 65 REGISTER_LOCAL_ANIMATED_PROPERTY(href) 66 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) 67 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement) 68 END_REGISTER_ANIMATED_PROPERTIES 69 70 inline SVGAElement::SVGAElement(const QualifiedName& tagName, Document* document) 71 : SVGGraphicsElement(tagName, document) 72 { 73 ASSERT(hasTagName(SVGNames::aTag)); 74 ScriptWrappable::init(this); 75 registerAnimatedPropertiesForSVGAElement(); 76 } 77 78 PassRefPtr<SVGAElement> SVGAElement::create(const QualifiedName& tagName, Document* document) 79 { 80 return adoptRef(new SVGAElement(tagName, document)); 81 } 82 83 String SVGAElement::title() const 84 { 85 // If the xlink:title is set (non-empty string), use it. 86 const AtomicString& title = fastGetAttribute(XLinkNames::titleAttr); 87 if (!title.isEmpty()) 88 return title; 89 90 // Otherwise, use the title of this element. 91 return SVGElement::title(); 92 } 93 94 bool SVGAElement::isSupportedAttribute(const QualifiedName& attrName) 95 { 96 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 97 if (supportedAttributes.isEmpty()) { 98 SVGURIReference::addSupportedAttributes(supportedAttributes); 99 SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes); 100 supportedAttributes.add(SVGNames::targetAttr); 101 } 102 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 103 } 104 105 void SVGAElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 106 { 107 if (!isSupportedAttribute(name)) { 108 SVGGraphicsElement::parseAttribute(name, value); 109 return; 110 } 111 112 if (name == SVGNames::targetAttr) { 113 setSVGTargetBaseValue(value); 114 return; 115 } 116 117 if (SVGURIReference::parseAttribute(name, value)) 118 return; 119 if (SVGExternalResourcesRequired::parseAttribute(name, value)) 120 return; 121 122 ASSERT_NOT_REACHED(); 123 } 124 125 void SVGAElement::svgAttributeChanged(const QualifiedName& attrName) 126 { 127 if (!isSupportedAttribute(attrName)) { 128 SVGGraphicsElement::svgAttributeChanged(attrName); 129 return; 130 } 131 132 SVGElementInstance::InvalidationGuard invalidationGuard(this); 133 134 // Unlike other SVG*Element classes, SVGAElement only listens to SVGURIReference changes 135 // as none of the other properties changes the linking behaviour for our <a> element. 136 if (SVGURIReference::isKnownAttribute(attrName)) { 137 bool wasLink = isLink(); 138 setIsLink(!hrefCurrentValue().isNull()); 139 140 if (wasLink != isLink()) 141 setNeedsStyleRecalc(); 142 } 143 } 144 145 RenderObject* SVGAElement::createRenderer(RenderStyle*) 146 { 147 if (parentNode() && parentNode()->isSVGElement() && toSVGElement(parentNode())->isTextContent()) 148 return new RenderSVGInline(this); 149 150 return new RenderSVGTransformableContainer(this); 151 } 152 153 void SVGAElement::defaultEventHandler(Event* event) 154 { 155 if (isLink()) { 156 if (focused() && isEnterKeyKeydownEvent(event)) { 157 event->setDefaultHandled(); 158 dispatchSimulatedClick(event); 159 return; 160 } 161 162 if (isLinkClick(event)) { 163 String url = stripLeadingAndTrailingHTMLSpaces(hrefCurrentValue()); 164 165 if (url[0] == '#') { 166 Element* targetElement = treeScope()->getElementById(url.substring(1)); 167 if (SVGSMILElement::isSMILElement(targetElement)) { 168 toSVGSMILElement(targetElement)->beginByLinkActivation(); 169 event->setDefaultHandled(); 170 return; 171 } 172 // Only allow navigation to internal <view> anchors. 173 if (targetElement && !targetElement->hasTagName(SVGNames::viewTag)) 174 return; 175 } 176 177 String target = this->target(); 178 if (target.isEmpty() && fastGetAttribute(XLinkNames::showAttr) == "new") 179 target = "_blank"; 180 event->setDefaultHandled(); 181 182 Frame* frame = document()->frame(); 183 if (!frame) 184 return; 185 FrameLoadRequest frameRequest(document()->securityOrigin(), ResourceRequest(document()->completeURL(url)), target); 186 frameRequest.setTriggeringEvent(event); 187 frame->loader()->load(frameRequest); 188 return; 189 } 190 } 191 192 SVGGraphicsElement::defaultEventHandler(event); 193 } 194 195 bool SVGAElement::supportsFocus() const 196 { 197 if (rendererIsEditable()) 198 return SVGGraphicsElement::supportsFocus(); 199 return true; 200 } 201 202 bool SVGAElement::rendererIsFocusable() const 203 { 204 if (renderer() && renderer()->absoluteClippedOverflowRect().isEmpty()) 205 return false; 206 207 return SVGElement::rendererIsFocusable(); 208 } 209 210 bool SVGAElement::isURLAttribute(const Attribute& attribute) const 211 { 212 return attribute.name().localName() == hrefAttr || SVGGraphicsElement::isURLAttribute(attribute); 213 } 214 215 bool SVGAElement::isMouseFocusable() const 216 { 217 return false; 218 } 219 220 bool SVGAElement::isKeyboardFocusable() const 221 { 222 if (!isFocusable()) 223 return false; 224 225 Page* page = document()->page(); 226 if (!page) 227 return false; 228 229 return page->chrome().client()->tabsToLinks(); 230 } 231 232 bool 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