2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2000 Simon Hausmann <hausmann@kde.org>
5 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
6 * (C) 2006 Graham Dennis (graham.dennis@gmail.com)
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
25 #include "core/html/HTMLAnchorElement.h"
27 #include "bindings/core/v8/V8DOMActivityLogger.h"
28 #include "core/dom/Attribute.h"
29 #include "core/editing/FrameSelection.h"
30 #include "core/events/KeyboardEvent.h"
31 #include "core/events/MouseEvent.h"
32 #include "core/frame/FrameHost.h"
33 #include "core/frame/LocalFrame.h"
34 #include "core/frame/Settings.h"
35 #include "core/frame/UseCounter.h"
36 #include "core/html/HTMLFormElement.h"
37 #include "core/html/HTMLImageElement.h"
38 #include "core/html/parser/HTMLParserIdioms.h"
39 #include "core/loader/FrameLoadRequest.h"
40 #include "core/loader/FrameLoader.h"
41 #include "core/loader/FrameLoaderClient.h"
42 #include "core/loader/FrameLoaderTypes.h"
43 #include "core/loader/PingLoader.h"
44 #include "core/page/Chrome.h"
45 #include "core/page/ChromeClient.h"
46 #include "core/rendering/RenderImage.h"
47 #include "platform/PlatformMouseEvent.h"
48 #include "platform/network/DNS.h"
49 #include "platform/network/ResourceRequest.h"
50 #include "platform/weborigin/KnownPorts.h"
51 #include "platform/weborigin/SecurityOrigin.h"
52 #include "platform/weborigin/SecurityPolicy.h"
53 #include "public/platform/Platform.h"
54 #include "public/platform/WebURL.h"
55 #include "public/platform/WebURLRequest.h"
56 #include "wtf/text/StringBuilder.h"
60 using namespace HTMLNames;
62 HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tagName, Document& document)
63 : HTMLElement(tagName, document)
65 , m_cachedVisitedLinkHash(0)
66 , m_wasFocusedByMouse(false)
70 PassRefPtrWillBeRawPtr<HTMLAnchorElement> HTMLAnchorElement::create(Document& document)
72 return adoptRefWillBeNoop(new HTMLAnchorElement(aTag, document));
75 HTMLAnchorElement::~HTMLAnchorElement()
79 bool HTMLAnchorElement::supportsFocus() const
81 if (hasEditableStyle())
82 return HTMLElement::supportsFocus();
83 // If not a link we should still be able to focus the element if it has tabIndex.
84 return isLink() || HTMLElement::supportsFocus();
87 bool HTMLAnchorElement::shouldHaveFocusAppearance() const
89 return !m_wasFocusedByMouse || HTMLElement::supportsFocus();
92 void HTMLAnchorElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type)
94 if (type != FocusTypePage)
95 m_wasFocusedByMouse = type == FocusTypeMouse;
96 HTMLElement::dispatchFocusEvent(oldFocusedElement, type);
99 bool HTMLAnchorElement::isMouseFocusable() const
102 return supportsFocus();
104 return HTMLElement::isMouseFocusable();
107 bool HTMLAnchorElement::isKeyboardFocusable() const
109 ASSERT(document().isActive());
111 if (isFocusable() && Element::supportsFocus())
112 return HTMLElement::isKeyboardFocusable();
114 if (isLink() && !document().frameHost()->chrome().client().tabsToLinks())
116 return HTMLElement::isKeyboardFocusable();
119 static void appendServerMapMousePosition(StringBuilder& url, Event* event)
121 if (!event->isMouseEvent())
124 ASSERT(event->target());
125 Node* target = event->target()->toNode();
127 if (!isHTMLImageElement(*target))
130 HTMLImageElement& imageElement = toHTMLImageElement(*target);
131 if (!imageElement.isServerMap())
134 if (!imageElement.renderer() || !imageElement.renderer()->isRenderImage())
136 RenderImage* renderer = toRenderImage(imageElement.renderer());
138 // FIXME: This should probably pass true for useTransforms.
139 FloatPoint absolutePosition = renderer->absoluteToLocal(FloatPoint(toMouseEvent(event)->pageX(), toMouseEvent(event)->pageY()));
140 int x = absolutePosition.x();
141 int y = absolutePosition.y();
148 void HTMLAnchorElement::defaultEventHandler(Event* event)
151 ASSERT(event->target());
152 Node* target = event->target()->toNode();
154 if ((focused() || target->focused()) && isEnterKeyKeypressEvent(event)) {
155 event->setDefaultHandled();
156 dispatchSimulatedClick(event);
160 if (isLinkClick(event)) {
166 HTMLElement::defaultEventHandler(event);
169 void HTMLAnchorElement::setActive(bool down)
171 if (hasEditableStyle())
174 ContainerNode::setActive(down);
177 void HTMLAnchorElement::attributeWillChange(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
179 if (name == hrefAttr && inDocument()) {
180 V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
181 if (activityLogger) {
184 argv.append(hrefAttr.toString());
185 argv.append(oldValue);
186 argv.append(newValue);
187 activityLogger->logEvent("blinkSetAttribute", argv.size(), argv.data());
190 HTMLElement::attributeWillChange(name, oldValue, newValue);
193 void HTMLAnchorElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
195 if (name == hrefAttr) {
196 bool wasLink = isLink();
197 setIsLink(!value.isNull());
198 if (wasLink || isLink()) {
199 pseudoStateChanged(CSSSelector::PseudoLink);
200 pseudoStateChanged(CSSSelector::PseudoVisited);
202 if (wasLink && !isLink() && treeScope().adjustedFocusedElement() == this) {
203 // We might want to call blur(), but it's dangerous to dispatch
205 document().setNeedsFocusedElementCheck();
208 String parsedURL = stripLeadingAndTrailingHTMLSpaces(value);
209 if (document().isDNSPrefetchEnabled()) {
210 if (protocolIs(parsedURL, "http") || protocolIs(parsedURL, "https") || parsedURL.startsWith("//"))
211 prefetchDNS(document().completeURL(parsedURL).host());
214 invalidateCachedVisitedLinkHash();
215 } else if (name == nameAttr || name == titleAttr) {
217 } else if (name == relAttr)
220 HTMLElement::parseAttribute(name, value);
223 void HTMLAnchorElement::accessKeyAction(bool sendMouseEvents)
225 dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
228 bool HTMLAnchorElement::isURLAttribute(const Attribute& attribute) const
230 return attribute.name().localName() == hrefAttr || HTMLElement::isURLAttribute(attribute);
233 bool HTMLAnchorElement::hasLegalLinkAttribute(const QualifiedName& name) const
235 return name == hrefAttr || HTMLElement::hasLegalLinkAttribute(name);
238 bool HTMLAnchorElement::canStartSelection() const
241 return HTMLElement::canStartSelection();
242 return hasEditableStyle();
245 bool HTMLAnchorElement::draggable() const
247 // Should be draggable if we have an href attribute.
248 const AtomicString& value = getAttribute(draggableAttr);
249 if (equalIgnoringCase(value, "true"))
251 if (equalIgnoringCase(value, "false"))
253 return hasAttribute(hrefAttr);
256 KURL HTMLAnchorElement::href() const
258 return document().completeURL(stripLeadingAndTrailingHTMLSpaces(getAttribute(hrefAttr)));
261 void HTMLAnchorElement::setHref(const AtomicString& value)
263 setAttribute(hrefAttr, value);
266 KURL HTMLAnchorElement::url() const
271 void HTMLAnchorElement::setURL(const KURL& url)
273 setHref(AtomicString(url.string()));
276 String HTMLAnchorElement::input() const
278 return getAttribute(hrefAttr);
281 void HTMLAnchorElement::setInput(const String& value)
283 setHref(AtomicString(value));
286 bool HTMLAnchorElement::hasRel(uint32_t relation) const
288 return m_linkRelations & relation;
291 void HTMLAnchorElement::setRel(const AtomicString& value)
294 SpaceSplitString newLinkRelations(value, true);
295 // FIXME: Add link relations as they are implemented
296 if (newLinkRelations.contains("noreferrer"))
297 m_linkRelations |= RelationNoReferrer;
300 const AtomicString& HTMLAnchorElement::name() const
302 return getNameAttribute();
305 short HTMLAnchorElement::tabIndex() const
307 // Skip the supportsFocus check in HTMLElement.
308 return Element::tabIndex();
311 bool HTMLAnchorElement::isLiveLink() const
313 return isLink() && !hasEditableStyle();
316 void HTMLAnchorElement::sendPings(const KURL& destinationURL) const
318 const AtomicString& pingValue = getAttribute(pingAttr);
319 if (pingValue.isNull() || !document().settings() || !document().settings()->hyperlinkAuditingEnabled())
322 UseCounter::count(document(), UseCounter::HTMLAnchorElementPingAttribute);
324 SpaceSplitString pingURLs(pingValue, false);
325 for (unsigned i = 0; i < pingURLs.size(); i++)
326 PingLoader::sendLinkAuditPing(document().frame(), document().completeURL(pingURLs[i]), destinationURL);
329 void HTMLAnchorElement::handleClick(Event* event)
331 event->setDefaultHandled();
333 LocalFrame* frame = document().frame();
338 url.append(stripLeadingAndTrailingHTMLSpaces(fastGetAttribute(hrefAttr)));
339 appendServerMapMousePosition(url, event);
340 KURL completedURL = document().completeURL(url.toString());
342 // Schedule the ping before the frame load. Prerender in Chrome may kill the renderer as soon as the navigation is
344 sendPings(completedURL);
346 ResourceRequest request(completedURL);
347 if (hasAttribute(downloadAttr)) {
348 request.setRequestContext(blink::WebURLRequest::RequestContextDownload);
349 bool isSameOrigin = completedURL.protocolIsData() || document().securityOrigin()->canRequest(completedURL);
350 const AtomicString& suggestedName = (isSameOrigin ? fastGetAttribute(downloadAttr) : nullAtom);
352 frame->loader().client()->loadURLExternally(request, NavigationPolicyDownload, suggestedName);
354 request.setRequestContext(blink::WebURLRequest::RequestContextHyperlink);
355 FrameLoadRequest frameRequest(&document(), request, getAttribute(targetAttr));
356 frameRequest.setTriggeringEvent(event);
357 if (hasRel(RelationNoReferrer))
358 frameRequest.setShouldSendReferrer(NeverSendReferrer);
359 frame->loader().load(frameRequest);
363 bool isEnterKeyKeypressEvent(Event* event)
365 return event->type() == EventTypeNames::keypress && event->isKeyboardEvent() && toKeyboardEvent(event)->keyIdentifier() == "Enter";
368 bool isLinkClick(Event* event)
370 return event->type() == EventTypeNames::click && (!event->isMouseEvent() || toMouseEvent(event)->button() != RightButton);
373 bool HTMLAnchorElement::willRespondToMouseClickEvents()
375 return isLink() || HTMLElement::willRespondToMouseClickEvents();
378 bool HTMLAnchorElement::isInteractiveContent() const
383 Node::InsertionNotificationRequest HTMLAnchorElement::insertedInto(ContainerNode* insertionPoint)
385 if (insertionPoint->inDocument()) {
386 V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
387 if (activityLogger) {
390 argv.append(fastGetAttribute(hrefAttr));
391 activityLogger->logEvent("blinkAddElement", argv.size(), argv.data());
394 return HTMLElement::insertedInto(insertionPoint);