/* * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann * Copyright (C) 2004, 2005, 2007 Rob Buis * Copyright (C) 2007 Eric Seidel * Copyright (C) 2010-2019 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "SVGAElement.h" #include "DOMTokenList.h" #include "Document.h" #include "EventHandler.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderTypes.h" #include "HTMLAnchorElement.h" #include "HTMLParserIdioms.h" #include "KeyboardEvent.h" #include "LegacyRenderSVGTransformableContainer.h" #include "MouseEvent.h" #include "PlatformMouseEvent.h" #include "RenderSVGInline.h" #include "RenderSVGText.h" #include "ResourceRequest.h" #include "SVGElementInlines.h" #include "SVGElementTypeHelpers.h" #include "SVGNames.h" #include "SVGSMILElement.h" #include "XLinkNames.h" #include namespace WebCore { WTF_MAKE_ISO_ALLOCATED_IMPL(SVGAElement); inline SVGAElement::SVGAElement(const QualifiedName& tagName, Document& document) : SVGGraphicsElement(tagName, document) , SVGURIReference(this) { ASSERT(hasTagName(SVGNames::aTag)); static std::once_flag onceFlag; std::call_once(onceFlag, [] { PropertyRegistry::registerProperty(); }); } Ref SVGAElement::create(const QualifiedName& tagName, Document& document) { return adoptRef(*new SVGAElement(tagName, document)); } String SVGAElement::title() const { // If the xlink:title is set (non-empty string), use it. const AtomString& title = attributeWithoutSynchronization(XLinkNames::titleAttr); if (!title.isEmpty()) return title; // Otherwise, use the title of this element. return SVGElement::title(); } void SVGAElement::parseAttribute(const QualifiedName& name, const AtomString& value) { if (name == SVGNames::targetAttr) { m_target->setBaseValInternal(value); return; } else if (name == SVGNames::relAttr) { if (m_relList) m_relList->associatedAttributeValueChanged(value); } SVGGraphicsElement::parseAttribute(name, value); SVGURIReference::parseAttribute(name, value); } void SVGAElement::svgAttributeChanged(const QualifiedName& attrName) { if (SVGURIReference::isKnownAttribute(attrName)) { bool wasLink = isLink(); setIsLink(!href().isNull() && !shouldProhibitLinks(this)); if (wasLink != isLink()) { InstanceInvalidationGuard guard(*this); invalidateStyleForSubtree(); } return; } SVGGraphicsElement::svgAttributeChanged(attrName); } RenderPtr SVGAElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) { if (is(parentNode()) && downcast(*parentNode()).isTextContent()) return createRenderer(*this, WTFMove(style)); return createRenderer(*this, WTFMove(style)); } void SVGAElement::defaultEventHandler(Event& event) { if (isLink()) { if (focused() && isEnterKeyKeydownEvent(event)) { event.setDefaultHandled(); dispatchSimulatedClick(&event); return; } if (MouseEvent::canTriggerActivationBehavior(event)) { String url = stripLeadingAndTrailingHTMLSpaces(href()); if (url[0] == '#') { RefPtr targetElement = treeScope().getElementById(url.substringSharingImpl(1)); if (is(targetElement)) { downcast(*targetElement).beginByLinkActivation(); event.setDefaultHandled(); return; } } auto target = this->target(); if (target.isEmpty() && attributeWithoutSynchronization(XLinkNames::showAttr) == "new"_s) target = blankTargetFrameName(); event.setDefaultHandled(); RefPtr frame = document().frame(); if (!frame) return; frame->loader().changeLocation(document().completeURL(url), target, &event, ReferrerPolicy::EmptyString, document().shouldOpenExternalURLsPolicyToPropagate()); return; } } SVGGraphicsElement::defaultEventHandler(event); } int SVGAElement::defaultTabIndex() const { return 0; } bool SVGAElement::supportsFocus() const { if (hasEditableStyle()) return SVGGraphicsElement::supportsFocus(); // If not a link we should still be able to focus the element if it has a tabIndex. return isLink() || SVGGraphicsElement::supportsFocus(); } bool SVGAElement::isURLAttribute(const Attribute& attribute) const { return SVGURIReference::isKnownAttribute(attribute.name()) || SVGGraphicsElement::isURLAttribute(attribute); } bool SVGAElement::isMouseFocusable() const { // Links are focusable by default, but only allow links with tabindex or contenteditable to be mouse focusable. // https://bugs.webkit.org/show_bug.cgi?id=26856 if (isLink()) return Element::supportsFocus(); return SVGElement::isMouseFocusable(); } bool SVGAElement::isKeyboardFocusable(KeyboardEvent* event) const { if (isFocusable() && Element::supportsFocus()) return SVGElement::isKeyboardFocusable(event); if (isLink()) return document().frame()->eventHandler().tabsToLinks(event); return SVGElement::isKeyboardFocusable(event); } bool SVGAElement::canStartSelection() const { if (!isLink()) return SVGElement::canStartSelection(); return hasEditableStyle(); } bool SVGAElement::childShouldCreateRenderer(const Node& child) const { // http://www.w3.org/2003/01/REC-SVG11-20030114-errata#linking-text-environment // The 'a' element may contain any element that its parent may contain, except itself. if (child.hasTagName(SVGNames::aTag)) return false; if (parentElement() && parentElement()->isSVGElement()) return parentElement()->childShouldCreateRenderer(child); return SVGElement::childShouldCreateRenderer(child); } bool SVGAElement::willRespondToMouseClickEventsWithEditability(Editability editability) const { return isLink() || SVGGraphicsElement::willRespondToMouseClickEventsWithEditability(editability); } SharedStringHash SVGAElement::visitedLinkHash() const { ASSERT(isLink()); if (!m_storedVisitedLinkHash) m_storedVisitedLinkHash = computeVisitedLinkHash(document().baseURL(), getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr)); return *m_storedVisitedLinkHash; } DOMTokenList& SVGAElement::relList() { if (!m_relList) { m_relList = makeUnique(*this, SVGNames::relAttr, [](Document&, StringView token) { #if USE(SYSTEM_PREVIEW) if (equalLettersIgnoringASCIICase(token, "ar"_s)) return true; #endif return equalLettersIgnoringASCIICase(token, "noreferrer"_s) || equalLettersIgnoringASCIICase(token, "noopener"_s); }); } return *m_relList; } } // namespace WebCore