Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLAnchorElement.cpp
bloba5b29fa83b4746787b77ea9b5a6ab09ed6f15fe2
1 /*
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.
24 #include "config.h"
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"
44 namespace blink {
46 using namespace HTMLNames;
48 HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tagName, Document& document)
49 : HTMLElement(tagName, document)
50 , m_linkRelations(0)
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
94 if (isLink())
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())
108 return false;
109 return HTMLElement::isKeyboardFocusable();
112 static void appendServerMapMousePosition(StringBuilder& url, Event* event)
114 if (!event->isMouseEvent())
115 return;
117 ASSERT(event->target());
118 Node* target = event->target()->toNode();
119 ASSERT(target);
120 if (!isHTMLImageElement(*target))
121 return;
123 HTMLImageElement& imageElement = toHTMLImageElement(*target);
124 if (!imageElement.isServerMap())
125 return;
127 LayoutObject* layoutObject = imageElement.layoutObject();
128 if (!layoutObject || !layoutObject->isBox())
129 return;
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();
148 url.append('?');
149 url.appendNumber(clampedPoint.x());
150 url.append(',');
151 url.appendNumber(clampedPoint.y());
154 void HTMLAnchorElement::defaultEventHandler(Event* event)
156 if (isLink()) {
157 if (focused() && isEnterKeyKeydownEvent(event) && isLiveLink()) {
158 event->setDefaultHandled();
159 dispatchSimulatedClick(event);
160 return;
163 if (isLinkClick(event) && isLiveLink()) {
164 handleClick(event);
165 return;
169 HTMLElement::defaultEventHandler(event);
172 void HTMLAnchorElement::setActive(bool down)
174 if (hasEditableStyle())
175 return;
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) {
185 Vector<String> argv;
186 argv.append("a");
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
208 // events here.
209 document().setNeedsFocusedElementCheck();
211 if (isLink()) {
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) {
220 // Do nothing.
221 } else if (name == relAttr) {
222 setRel(value);
223 } else {
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
245 if (!isLink())
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"))
255 return true;
256 if (equalIgnoringCase(value, "false"))
257 return 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
273 return href();
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)
298 m_linkRelations = 0;
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())
325 return;
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();
339 if (!frame)
340 return;
342 StringBuilder url;
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
348 // sent out.
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);
366 } else {
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
393 return isLink();
396 Node::InsertionNotificationRequest HTMLAnchorElement::insertedInto(ContainerNode* insertionPoint)
398 if (insertionPoint->inDocument()) {
399 V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
400 if (activityLogger) {
401 Vector<String> argv;
402 argv.append("a");
403 argv.append(fastGetAttribute(hrefAttr));
404 activityLogger->logEvent("blinkAddElement", argv.size(), argv.data());
407 return HTMLElement::insertedInto(insertionPoint);
410 } // namespace blink