• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
7  *
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.
12  *
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.
17  *
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.
22  */
23 
24 #include "config.h"
25 #include "core/html/HTMLAnchorElement.h"
26 
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"
57 
58 namespace blink {
59 
60 using namespace HTMLNames;
61 
HTMLAnchorElement(const QualifiedName & tagName,Document & document)62 HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tagName, Document& document)
63     : HTMLElement(tagName, document)
64     , m_linkRelations(0)
65     , m_cachedVisitedLinkHash(0)
66     , m_wasFocusedByMouse(false)
67 {
68 }
69 
create(Document & document)70 PassRefPtrWillBeRawPtr<HTMLAnchorElement> HTMLAnchorElement::create(Document& document)
71 {
72     return adoptRefWillBeNoop(new HTMLAnchorElement(aTag, document));
73 }
74 
~HTMLAnchorElement()75 HTMLAnchorElement::~HTMLAnchorElement()
76 {
77 }
78 
supportsFocus() const79 bool HTMLAnchorElement::supportsFocus() const
80 {
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();
85 }
86 
shouldHaveFocusAppearance() const87 bool HTMLAnchorElement::shouldHaveFocusAppearance() const
88 {
89     return !m_wasFocusedByMouse || HTMLElement::supportsFocus();
90 }
91 
dispatchFocusEvent(Element * oldFocusedElement,FocusType type)92 void HTMLAnchorElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type)
93 {
94     if (type != FocusTypePage)
95         m_wasFocusedByMouse = type == FocusTypeMouse;
96     HTMLElement::dispatchFocusEvent(oldFocusedElement, type);
97 }
98 
isMouseFocusable() const99 bool HTMLAnchorElement::isMouseFocusable() const
100 {
101     if (isLink())
102         return supportsFocus();
103 
104     return HTMLElement::isMouseFocusable();
105 }
106 
isKeyboardFocusable() const107 bool HTMLAnchorElement::isKeyboardFocusable() const
108 {
109     ASSERT(document().isActive());
110 
111     if (isFocusable() && Element::supportsFocus())
112         return HTMLElement::isKeyboardFocusable();
113 
114     if (isLink() && !document().frameHost()->chrome().client().tabsToLinks())
115         return false;
116     return HTMLElement::isKeyboardFocusable();
117 }
118 
appendServerMapMousePosition(StringBuilder & url,Event * event)119 static void appendServerMapMousePosition(StringBuilder& url, Event* event)
120 {
121     if (!event->isMouseEvent())
122         return;
123 
124     ASSERT(event->target());
125     Node* target = event->target()->toNode();
126     ASSERT(target);
127     if (!isHTMLImageElement(*target))
128         return;
129 
130     HTMLImageElement& imageElement = toHTMLImageElement(*target);
131     if (!imageElement.isServerMap())
132         return;
133 
134     if (!imageElement.renderer() || !imageElement.renderer()->isRenderImage())
135         return;
136     RenderImage* renderer = toRenderImage(imageElement.renderer());
137 
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();
142     url.append('?');
143     url.appendNumber(x);
144     url.append(',');
145     url.appendNumber(y);
146 }
147 
defaultEventHandler(Event * event)148 void HTMLAnchorElement::defaultEventHandler(Event* event)
149 {
150     if (isLink()) {
151         if (focused() && isEnterKeyKeydownEvent(event) && isLiveLink()) {
152             event->setDefaultHandled();
153             dispatchSimulatedClick(event);
154             return;
155         }
156 
157         if (isLinkClick(event) && isLiveLink()) {
158             handleClick(event);
159             return;
160         }
161     }
162 
163     HTMLElement::defaultEventHandler(event);
164 }
165 
setActive(bool down)166 void HTMLAnchorElement::setActive(bool down)
167 {
168     if (hasEditableStyle())
169         return;
170 
171     ContainerNode::setActive(down);
172 }
173 
attributeWillChange(const QualifiedName & name,const AtomicString & oldValue,const AtomicString & newValue)174 void HTMLAnchorElement::attributeWillChange(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
175 {
176     if (name == hrefAttr && inDocument()) {
177         V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
178         if (activityLogger) {
179             Vector<String> argv;
180             argv.append("a");
181             argv.append(hrefAttr.toString());
182             argv.append(oldValue);
183             argv.append(newValue);
184             activityLogger->logEvent("blinkSetAttribute", argv.size(), argv.data());
185         }
186     }
187     HTMLElement::attributeWillChange(name, oldValue, newValue);
188 }
189 
parseAttribute(const QualifiedName & name,const AtomicString & value)190 void HTMLAnchorElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
191 {
192     if (name == hrefAttr) {
193         bool wasLink = isLink();
194         setIsLink(!value.isNull());
195         if (wasLink || isLink()) {
196             pseudoStateChanged(CSSSelector::PseudoLink);
197             pseudoStateChanged(CSSSelector::PseudoVisited);
198             if (wasLink != isLink())
199                 pseudoStateChanged(CSSSelector::PseudoEnabled);
200         }
201         if (wasLink && !isLink() && treeScope().adjustedFocusedElement() == this) {
202             // We might want to call blur(), but it's dangerous to dispatch
203             // events here.
204             document().setNeedsFocusedElementCheck();
205         }
206         if (isLink()) {
207             String parsedURL = stripLeadingAndTrailingHTMLSpaces(value);
208             if (document().isDNSPrefetchEnabled()) {
209                 if (protocolIs(parsedURL, "http") || protocolIs(parsedURL, "https") || parsedURL.startsWith("//"))
210                     prefetchDNS(document().completeURL(parsedURL).host());
211             }
212         }
213         invalidateCachedVisitedLinkHash();
214     } else if (name == nameAttr || name == titleAttr) {
215         // Do nothing.
216     } else if (name == relAttr)
217         setRel(value);
218     else
219         HTMLElement::parseAttribute(name, value);
220 }
221 
accessKeyAction(bool sendMouseEvents)222 void HTMLAnchorElement::accessKeyAction(bool sendMouseEvents)
223 {
224     dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
225 }
226 
isURLAttribute(const Attribute & attribute) const227 bool HTMLAnchorElement::isURLAttribute(const Attribute& attribute) const
228 {
229     return attribute.name().localName() == hrefAttr || HTMLElement::isURLAttribute(attribute);
230 }
231 
hasLegalLinkAttribute(const QualifiedName & name) const232 bool HTMLAnchorElement::hasLegalLinkAttribute(const QualifiedName& name) const
233 {
234     return name == hrefAttr || HTMLElement::hasLegalLinkAttribute(name);
235 }
236 
canStartSelection() const237 bool HTMLAnchorElement::canStartSelection() const
238 {
239     if (!isLink())
240         return HTMLElement::canStartSelection();
241     return hasEditableStyle();
242 }
243 
draggable() const244 bool HTMLAnchorElement::draggable() const
245 {
246     // Should be draggable if we have an href attribute.
247     const AtomicString& value = getAttribute(draggableAttr);
248     if (equalIgnoringCase(value, "true"))
249         return true;
250     if (equalIgnoringCase(value, "false"))
251         return false;
252     return hasAttribute(hrefAttr);
253 }
254 
href() const255 KURL HTMLAnchorElement::href() const
256 {
257     return document().completeURL(stripLeadingAndTrailingHTMLSpaces(getAttribute(hrefAttr)));
258 }
259 
setHref(const AtomicString & value)260 void HTMLAnchorElement::setHref(const AtomicString& value)
261 {
262     setAttribute(hrefAttr, value);
263 }
264 
url() const265 KURL HTMLAnchorElement::url() const
266 {
267     return href();
268 }
269 
setURL(const KURL & url)270 void HTMLAnchorElement::setURL(const KURL& url)
271 {
272     setHref(AtomicString(url.string()));
273 }
274 
input() const275 String HTMLAnchorElement::input() const
276 {
277     return getAttribute(hrefAttr);
278 }
279 
setInput(const String & value)280 void HTMLAnchorElement::setInput(const String& value)
281 {
282     setHref(AtomicString(value));
283 }
284 
hasRel(uint32_t relation) const285 bool HTMLAnchorElement::hasRel(uint32_t relation) const
286 {
287     return m_linkRelations & relation;
288 }
289 
setRel(const AtomicString & value)290 void HTMLAnchorElement::setRel(const AtomicString& value)
291 {
292     m_linkRelations = 0;
293     SpaceSplitString newLinkRelations(value, true);
294     // FIXME: Add link relations as they are implemented
295     if (newLinkRelations.contains("noreferrer"))
296         m_linkRelations |= RelationNoReferrer;
297 }
298 
name() const299 const AtomicString& HTMLAnchorElement::name() const
300 {
301     return getNameAttribute();
302 }
303 
tabIndex() const304 short HTMLAnchorElement::tabIndex() const
305 {
306     // Skip the supportsFocus check in HTMLElement.
307     return Element::tabIndex();
308 }
309 
isLiveLink() const310 bool HTMLAnchorElement::isLiveLink() const
311 {
312     return isLink() && !hasEditableStyle();
313 }
314 
sendPings(const KURL & destinationURL) const315 void HTMLAnchorElement::sendPings(const KURL& destinationURL) const
316 {
317     const AtomicString& pingValue = getAttribute(pingAttr);
318     if (pingValue.isNull() || !document().settings() || !document().settings()->hyperlinkAuditingEnabled())
319         return;
320 
321     UseCounter::count(document(), UseCounter::HTMLAnchorElementPingAttribute);
322 
323     SpaceSplitString pingURLs(pingValue, false);
324     for (unsigned i = 0; i < pingURLs.size(); i++)
325         PingLoader::sendLinkAuditPing(document().frame(), document().completeURL(pingURLs[i]), destinationURL);
326 }
327 
handleClick(Event * event)328 void HTMLAnchorElement::handleClick(Event* event)
329 {
330     event->setDefaultHandled();
331 
332     LocalFrame* frame = document().frame();
333     if (!frame)
334         return;
335 
336     StringBuilder url;
337     url.append(stripLeadingAndTrailingHTMLSpaces(fastGetAttribute(hrefAttr)));
338     appendServerMapMousePosition(url, event);
339     KURL completedURL = document().completeURL(url.toString());
340 
341     // Schedule the ping before the frame load. Prerender in Chrome may kill the renderer as soon as the navigation is
342     // sent out.
343     sendPings(completedURL);
344 
345     ResourceRequest request(completedURL);
346     if (hasAttribute(downloadAttr)) {
347         request.setRequestContext(blink::WebURLRequest::RequestContextDownload);
348         if (!hasRel(RelationNoReferrer)) {
349             String referrer = SecurityPolicy::generateReferrerHeader(document().referrerPolicy(), completedURL, document().outgoingReferrer());
350             if (!referrer.isEmpty())
351                 request.setHTTPReferrer(Referrer(referrer, document().referrerPolicy()));
352         }
353 
354         bool isSameOrigin = completedURL.protocolIsData() || document().securityOrigin()->canRequest(completedURL);
355         const AtomicString& suggestedName = (isSameOrigin ? fastGetAttribute(downloadAttr) : nullAtom);
356 
357         frame->loader().client()->loadURLExternally(request, NavigationPolicyDownload, suggestedName);
358     } else {
359         request.setRequestContext(blink::WebURLRequest::RequestContextHyperlink);
360         FrameLoadRequest frameRequest(&document(), request, getAttribute(targetAttr));
361         frameRequest.setTriggeringEvent(event);
362         if (hasRel(RelationNoReferrer))
363             frameRequest.setShouldSendReferrer(NeverSendReferrer);
364         frame->loader().load(frameRequest);
365     }
366 }
367 
isEnterKeyKeydownEvent(Event * event)368 bool isEnterKeyKeydownEvent(Event* event)
369 {
370     return event->type() == EventTypeNames::keydown && event->isKeyboardEvent() && toKeyboardEvent(event)->keyIdentifier() == "Enter";
371 }
372 
isLinkClick(Event * event)373 bool isLinkClick(Event* event)
374 {
375     return event->type() == EventTypeNames::click && (!event->isMouseEvent() || toMouseEvent(event)->button() != RightButton);
376 }
377 
willRespondToMouseClickEvents()378 bool HTMLAnchorElement::willRespondToMouseClickEvents()
379 {
380     return isLink() || HTMLElement::willRespondToMouseClickEvents();
381 }
382 
isInteractiveContent() const383 bool HTMLAnchorElement::isInteractiveContent() const
384 {
385     return isLink();
386 }
387 
insertedInto(ContainerNode * insertionPoint)388 Node::InsertionNotificationRequest HTMLAnchorElement::insertedInto(ContainerNode* insertionPoint)
389 {
390     if (insertionPoint->inDocument()) {
391         V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
392         if (activityLogger) {
393             Vector<String> argv;
394             argv.append("a");
395             argv.append(fastGetAttribute(hrefAttr));
396             activityLogger->logEvent("blinkAddElement", argv.size(), argv.data());
397         }
398     }
399     return HTMLElement::insertedInto(insertionPoint);
400 }
401 
402 }
403