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/events/KeyboardEvent.h"
29 #include "core/events/MouseEvent.h"
30 #include "core/frame/FrameHost.h"
31 #include "core/frame/Settings.h"
32 #include "core/frame/UseCounter.h"
33 #include "core/html/HTMLImageElement.h"
34 #include "core/html/parser/HTMLParserIdioms.h"
35 #include "core/layout/LayoutImage.h"
36 #include "core/loader/FrameLoadRequest.h"
37 #include "core/loader/FrameLoaderClient.h"
38 #include "core/loader/PingLoader.h"
39 #include "core/page/ChromeClient.h"
40 #include "platform/RuntimeEnabledFeatures.h"
41 #include "platform/network/NetworkHints.h"
42 #include "platform/weborigin/SecurityPolicy.h"
46 using namespace HTMLNames
;
48 HTMLAnchorElement::HTMLAnchorElement(const QualifiedName
& tagName
, Document
& document
)
49 : HTMLElement(tagName
, document
)
51 , m_cachedVisitedLinkHash(0)
52 , m_wasFocusedByMouse(false)
56 PassRefPtrWillBeRawPtr
<HTMLAnchorElement
> HTMLAnchorElement::create(Document
& document
)
58 return adoptRefWillBeNoop(new HTMLAnchorElement(aTag
, document
));
61 HTMLAnchorElement::~HTMLAnchorElement()
65 bool HTMLAnchorElement::supportsFocus() const
67 if (hasEditableStyle())
68 return HTMLElement::supportsFocus();
69 // If not a link we should still be able to focus the element if it has tabIndex.
70 return isLink() || HTMLElement::supportsFocus();
73 bool HTMLAnchorElement::shouldHaveFocusAppearance() const
75 return !m_wasFocusedByMouse
|| HTMLElement::supportsFocus();
78 void HTMLAnchorElement::dispatchFocusEvent(Element
* oldFocusedElement
, WebFocusType type
, InputDeviceCapabilities
* sourceCapabilities
)
80 if (type
!= WebFocusTypePage
)
81 m_wasFocusedByMouse
= type
== WebFocusTypeMouse
;
82 HTMLElement::dispatchFocusEvent(oldFocusedElement
, type
, sourceCapabilities
);
85 void HTMLAnchorElement::dispatchBlurEvent(Element
* newFocusedElement
, WebFocusType type
, InputDeviceCapabilities
* sourceCapabilities
)
87 if (type
!= WebFocusTypePage
)
88 m_wasFocusedByMouse
= false;
89 HTMLElement::dispatchBlurEvent(newFocusedElement
, type
, sourceCapabilities
);
92 bool HTMLAnchorElement::isMouseFocusable() const
95 return supportsFocus();
97 return HTMLElement::isMouseFocusable();
100 bool HTMLAnchorElement::isKeyboardFocusable() const
102 ASSERT(document().isActive());
104 if (isFocusable() && Element::supportsFocus())
105 return HTMLElement::isKeyboardFocusable();
107 if (isLink() && !document().frameHost()->chromeClient().tabsToLinks())
109 return HTMLElement::isKeyboardFocusable();
112 static void appendServerMapMousePosition(StringBuilder
& url
, Event
* event
)
114 if (!event
->isMouseEvent())
117 ASSERT(event
->target());
118 Node
* target
= event
->target()->toNode();
120 if (!isHTMLImageElement(*target
))
123 HTMLImageElement
& imageElement
= toHTMLImageElement(*target
);
124 if (!imageElement
.isServerMap())
127 LayoutObject
* layoutObject
= imageElement
.layoutObject();
128 if (!layoutObject
|| !layoutObject
->isBox())
131 // The coordinates sent in the query string are relative to the height and
132 // width of the image element, ignoring CSS transform/zoom.
133 LayoutPoint
mapPoint(layoutObject
->absoluteToLocal(FloatPoint(toMouseEvent(event
)->absoluteLocation()), UseTransforms
));
135 // The origin (0,0) is at the upper left of the content area, inside the
136 // padding and border.
137 mapPoint
-= toLayoutBox(layoutObject
)->contentBoxOffset();
139 // CSS zoom is not reflected in the map coordinates.
140 float scaleFactor
= 1 / layoutObject
->style()->effectiveZoom();
141 mapPoint
.scale(scaleFactor
, scaleFactor
);
143 // Negative coordinates are clamped to 0 such that clicks in the left and
144 // top padding/border areas receive an X or Y coordinate of 0.
145 IntPoint
clampedPoint(roundedIntPoint(mapPoint
));
146 clampedPoint
.clampNegativeToZero();
149 url
.appendNumber(clampedPoint
.x());
151 url
.appendNumber(clampedPoint
.y());
154 void HTMLAnchorElement::defaultEventHandler(Event
* event
)
157 if (focused() && isEnterKeyKeydownEvent(event
) && isLiveLink()) {
158 event
->setDefaultHandled();
159 dispatchSimulatedClick(event
);
163 if (isLinkClick(event
) && isLiveLink()) {
169 HTMLElement::defaultEventHandler(event
);
172 void HTMLAnchorElement::setActive(bool down
)
174 if (hasEditableStyle())
177 ContainerNode::setActive(down
);
180 void HTMLAnchorElement::attributeWillChange(const QualifiedName
& name
, const AtomicString
& oldValue
, const AtomicString
& newValue
)
182 if (name
== hrefAttr
&& inDocument()) {
183 V8DOMActivityLogger
* activityLogger
= V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
184 if (activityLogger
) {
187 argv
.append(hrefAttr
.toString());
188 argv
.append(oldValue
);
189 argv
.append(newValue
);
190 activityLogger
->logEvent("blinkSetAttribute", argv
.size(), argv
.data());
193 HTMLElement::attributeWillChange(name
, oldValue
, newValue
);
196 void HTMLAnchorElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
198 if (name
== hrefAttr
) {
199 bool wasLink
= isLink();
200 setIsLink(!value
.isNull());
201 if (wasLink
|| isLink()) {
202 pseudoStateChanged(CSSSelector::PseudoLink
);
203 pseudoStateChanged(CSSSelector::PseudoVisited
);
204 pseudoStateChanged(CSSSelector::PseudoAnyLink
);
206 if (wasLink
&& !isLink() && treeScope().adjustedFocusedElement() == this) {
207 // We might want to call blur(), but it's dangerous to dispatch
209 document().setNeedsFocusedElementCheck();
212 String parsedURL
= stripLeadingAndTrailingHTMLSpaces(value
);
213 if (document().isDNSPrefetchEnabled()) {
214 if (protocolIs(parsedURL
, "http") || protocolIs(parsedURL
, "https") || parsedURL
.startsWith("//"))
215 prefetchDNS(document().completeURL(parsedURL
).host());
218 invalidateCachedVisitedLinkHash();
219 } else if (name
== nameAttr
|| name
== titleAttr
) {
221 } else if (name
== relAttr
) {
224 HTMLElement::parseAttribute(name
, value
);
228 void HTMLAnchorElement::accessKeyAction(bool sendMouseEvents
)
230 dispatchSimulatedClick(0, sendMouseEvents
? SendMouseUpDownEvents
: SendNoEvents
);
233 bool HTMLAnchorElement::isURLAttribute(const Attribute
& attribute
) const
235 return attribute
.name().localName() == hrefAttr
|| HTMLElement::isURLAttribute(attribute
);
238 bool HTMLAnchorElement::hasLegalLinkAttribute(const QualifiedName
& name
) const
240 return name
== hrefAttr
|| HTMLElement::hasLegalLinkAttribute(name
);
243 bool HTMLAnchorElement::canStartSelection() const
246 return HTMLElement::canStartSelection();
247 return hasEditableStyle();
250 bool HTMLAnchorElement::draggable() const
252 // Should be draggable if we have an href attribute.
253 const AtomicString
& value
= getAttribute(draggableAttr
);
254 if (equalIgnoringCase(value
, "true"))
256 if (equalIgnoringCase(value
, "false"))
258 return hasAttribute(hrefAttr
);
261 KURL
HTMLAnchorElement::href() const
263 return document().completeURL(stripLeadingAndTrailingHTMLSpaces(getAttribute(hrefAttr
)));
266 void HTMLAnchorElement::setHref(const AtomicString
& value
)
268 setAttribute(hrefAttr
, value
);
271 KURL
HTMLAnchorElement::url() const
276 void HTMLAnchorElement::setURL(const KURL
& url
)
278 setHref(AtomicString(url
.string()));
281 String
HTMLAnchorElement::input() const
283 return getAttribute(hrefAttr
);
286 void HTMLAnchorElement::setInput(const String
& value
)
288 setHref(AtomicString(value
));
291 bool HTMLAnchorElement::hasRel(uint32_t relation
) const
293 return m_linkRelations
& relation
;
296 void HTMLAnchorElement::setRel(const AtomicString
& value
)
299 SpaceSplitString
newLinkRelations(value
, SpaceSplitString::ShouldFoldCase
);
300 // FIXME: Add link relations as they are implemented
301 if (newLinkRelations
.contains("noreferrer"))
302 m_linkRelations
|= RelationNoReferrer
;
305 const AtomicString
& HTMLAnchorElement::name() const
307 return getNameAttribute();
310 short HTMLAnchorElement::tabIndex() const
312 // Skip the supportsFocus check in HTMLElement.
313 return Element::tabIndex();
316 bool HTMLAnchorElement::isLiveLink() const
318 return isLink() && !hasEditableStyle();
321 void HTMLAnchorElement::sendPings(const KURL
& destinationURL
) const
323 const AtomicString
& pingValue
= getAttribute(pingAttr
);
324 if (pingValue
.isNull() || !document().settings() || !document().settings()->hyperlinkAuditingEnabled())
327 UseCounter::count(document(), UseCounter::HTMLAnchorElementPingAttribute
);
329 SpaceSplitString
pingURLs(pingValue
, SpaceSplitString::ShouldNotFoldCase
);
330 for (unsigned i
= 0; i
< pingURLs
.size(); i
++)
331 PingLoader::sendLinkAuditPing(document().frame(), document().completeURL(pingURLs
[i
]), destinationURL
);
334 void HTMLAnchorElement::handleClick(Event
* event
)
336 event
->setDefaultHandled();
338 LocalFrame
* frame
= document().frame();
343 url
.append(stripLeadingAndTrailingHTMLSpaces(fastGetAttribute(hrefAttr
)));
344 appendServerMapMousePosition(url
, event
);
345 KURL completedURL
= document().completeURL(url
.toString());
347 // Schedule the ping before the frame load. Prerender in Chrome may kill the renderer as soon as the navigation is
349 sendPings(completedURL
);
351 ResourceRequest
request(completedURL
);
352 request
.setUIStartTime(event
->uiCreateTime());
353 request
.setInputPerfMetricReportPolicy(InputToLoadPerfMetricReportPolicy::ReportLink
);
355 ReferrerPolicy policy
;
356 if (RuntimeEnabledFeatures::referrerPolicyAttributeEnabled() && hasAttribute(referrerpolicyAttr
) && SecurityPolicy::referrerPolicyFromString(fastGetAttribute(referrerpolicyAttr
), &policy
) && !hasRel(RelationNoReferrer
)) {
357 request
.setHTTPReferrer(SecurityPolicy::generateReferrer(policy
, completedURL
, document().outgoingReferrer()));
360 if (hasAttribute(downloadAttr
)) {
361 request
.setRequestContext(WebURLRequest::RequestContextDownload
);
362 bool isSameOrigin
= completedURL
.protocolIsData() || document().securityOrigin()->canRequest(completedURL
);
363 const AtomicString
& suggestedName
= (isSameOrigin
? fastGetAttribute(downloadAttr
) : nullAtom
);
365 frame
->loader().client()->loadURLExternally(request
, NavigationPolicyDownload
, suggestedName
);
367 request
.setRequestContext(WebURLRequest::RequestContextHyperlink
);
368 FrameLoadRequest
frameRequest(&document(), request
, getAttribute(targetAttr
));
369 frameRequest
.setTriggeringEvent(event
);
370 if (hasRel(RelationNoReferrer
))
371 frameRequest
.setShouldSendReferrer(NeverSendReferrer
);
372 frame
->loader().load(frameRequest
);
376 bool isEnterKeyKeydownEvent(Event
* event
)
378 return event
->type() == EventTypeNames::keydown
&& event
->isKeyboardEvent() && toKeyboardEvent(event
)->keyIdentifier() == "Enter";
381 bool isLinkClick(Event
* event
)
383 return event
->type() == EventTypeNames::click
&& (!event
->isMouseEvent() || toMouseEvent(event
)->button() != RightButton
);
386 bool HTMLAnchorElement::willRespondToMouseClickEvents()
388 return isLink() || HTMLElement::willRespondToMouseClickEvents();
391 bool HTMLAnchorElement::isInteractiveContent() const
396 Node::InsertionNotificationRequest
HTMLAnchorElement::insertedInto(ContainerNode
* insertionPoint
)
398 if (insertionPoint
->inDocument()) {
399 V8DOMActivityLogger
* activityLogger
= V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
400 if (activityLogger
) {
403 argv
.append(fastGetAttribute(hrefAttr
));
404 activityLogger
->logEvent("blinkAddElement", argv
.size(), argv
.data());
407 return HTMLElement::insertedInto(insertionPoint
);