Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLAnchorElement.cpp
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
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
70 PassRefPtrWillBeRawPtr<HTMLAnchorElement> HTMLAnchorElement::create(Document& document)
71 {
72     return adoptRefWillBeNoop(new HTMLAnchorElement(aTag, document));
73 }
74
75 HTMLAnchorElement::~HTMLAnchorElement()
76 {
77 }
78
79 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
87 bool HTMLAnchorElement::shouldHaveFocusAppearance() const
88 {
89     return !m_wasFocusedByMouse || HTMLElement::supportsFocus();
90 }
91
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
99 bool HTMLAnchorElement::isMouseFocusable() const
100 {
101     if (isLink())
102         return supportsFocus();
103
104     return HTMLElement::isMouseFocusable();
105 }
106
107 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
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
148 void HTMLAnchorElement::defaultEventHandler(Event* event)
149 {
150     if (isLiveLink()) {
151         ASSERT(event->target());
152         Node* target = event->target()->toNode();
153         ASSERT(target);
154         if ((focused() || target->focused()) && isEnterKeyKeypressEvent(event)) {
155             event->setDefaultHandled();
156             dispatchSimulatedClick(event);
157             return;
158         }
159
160         if (isLinkClick(event)) {
161             handleClick(event);
162             return;
163         }
164     }
165
166     HTMLElement::defaultEventHandler(event);
167 }
168
169 void HTMLAnchorElement::setActive(bool down)
170 {
171     if (hasEditableStyle())
172         return;
173
174     ContainerNode::setActive(down);
175 }
176
177 void HTMLAnchorElement::attributeWillChange(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
178 {
179     if (name == hrefAttr && inDocument()) {
180         V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
181         if (activityLogger) {
182             Vector<String> argv;
183             argv.append("a");
184             argv.append(hrefAttr.toString());
185             argv.append(oldValue);
186             argv.append(newValue);
187             activityLogger->logEvent("blinkSetAttribute", argv.size(), argv.data());
188         }
189     }
190     HTMLElement::attributeWillChange(name, oldValue, newValue);
191 }
192
193 void HTMLAnchorElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
194 {
195     if (name == hrefAttr) {
196         bool wasLink = isLink();
197         setIsLink(!value.isNull());
198         if (wasLink || isLink()) {
199             pseudoStateChanged(CSSSelector::PseudoLink);
200             pseudoStateChanged(CSSSelector::PseudoVisited);
201         }
202         if (wasLink && !isLink() && treeScope().adjustedFocusedElement() == this) {
203             // We might want to call blur(), but it's dangerous to dispatch
204             // events here.
205             document().setNeedsFocusedElementCheck();
206         }
207         if (isLink()) {
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());
212             }
213         }
214         invalidateCachedVisitedLinkHash();
215     } else if (name == nameAttr || name == titleAttr) {
216         // Do nothing.
217     } else if (name == relAttr)
218         setRel(value);
219     else
220         HTMLElement::parseAttribute(name, value);
221 }
222
223 void HTMLAnchorElement::accessKeyAction(bool sendMouseEvents)
224 {
225     dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
226 }
227
228 bool HTMLAnchorElement::isURLAttribute(const Attribute& attribute) const
229 {
230     return attribute.name().localName() == hrefAttr || HTMLElement::isURLAttribute(attribute);
231 }
232
233 bool HTMLAnchorElement::hasLegalLinkAttribute(const QualifiedName& name) const
234 {
235     return name == hrefAttr || HTMLElement::hasLegalLinkAttribute(name);
236 }
237
238 bool HTMLAnchorElement::canStartSelection() const
239 {
240     if (!isLink())
241         return HTMLElement::canStartSelection();
242     return hasEditableStyle();
243 }
244
245 bool HTMLAnchorElement::draggable() const
246 {
247     // Should be draggable if we have an href attribute.
248     const AtomicString& value = getAttribute(draggableAttr);
249     if (equalIgnoringCase(value, "true"))
250         return true;
251     if (equalIgnoringCase(value, "false"))
252         return false;
253     return hasAttribute(hrefAttr);
254 }
255
256 KURL HTMLAnchorElement::href() const
257 {
258     return document().completeURL(stripLeadingAndTrailingHTMLSpaces(getAttribute(hrefAttr)));
259 }
260
261 void HTMLAnchorElement::setHref(const AtomicString& value)
262 {
263     setAttribute(hrefAttr, value);
264 }
265
266 KURL HTMLAnchorElement::url() const
267 {
268     return href();
269 }
270
271 void HTMLAnchorElement::setURL(const KURL& url)
272 {
273     setHref(AtomicString(url.string()));
274 }
275
276 String HTMLAnchorElement::input() const
277 {
278     return getAttribute(hrefAttr);
279 }
280
281 void HTMLAnchorElement::setInput(const String& value)
282 {
283     setHref(AtomicString(value));
284 }
285
286 bool HTMLAnchorElement::hasRel(uint32_t relation) const
287 {
288     return m_linkRelations & relation;
289 }
290
291 void HTMLAnchorElement::setRel(const AtomicString& value)
292 {
293     m_linkRelations = 0;
294     SpaceSplitString newLinkRelations(value, true);
295     // FIXME: Add link relations as they are implemented
296     if (newLinkRelations.contains("noreferrer"))
297         m_linkRelations |= RelationNoReferrer;
298 }
299
300 const AtomicString& HTMLAnchorElement::name() const
301 {
302     return getNameAttribute();
303 }
304
305 short HTMLAnchorElement::tabIndex() const
306 {
307     // Skip the supportsFocus check in HTMLElement.
308     return Element::tabIndex();
309 }
310
311 bool HTMLAnchorElement::isLiveLink() const
312 {
313     return isLink() && !hasEditableStyle();
314 }
315
316 void HTMLAnchorElement::sendPings(const KURL& destinationURL) const
317 {
318     const AtomicString& pingValue = getAttribute(pingAttr);
319     if (pingValue.isNull() || !document().settings() || !document().settings()->hyperlinkAuditingEnabled())
320         return;
321
322     UseCounter::count(document(), UseCounter::HTMLAnchorElementPingAttribute);
323
324     SpaceSplitString pingURLs(pingValue, false);
325     for (unsigned i = 0; i < pingURLs.size(); i++)
326         PingLoader::sendLinkAuditPing(document().frame(), document().completeURL(pingURLs[i]), destinationURL);
327 }
328
329 void HTMLAnchorElement::handleClick(Event* event)
330 {
331     event->setDefaultHandled();
332
333     LocalFrame* frame = document().frame();
334     if (!frame)
335         return;
336
337     StringBuilder url;
338     url.append(stripLeadingAndTrailingHTMLSpaces(fastGetAttribute(hrefAttr)));
339     appendServerMapMousePosition(url, event);
340     KURL completedURL = document().completeURL(url.toString());
341
342     // Schedule the ping before the frame load. Prerender in Chrome may kill the renderer as soon as the navigation is
343     // sent out.
344     sendPings(completedURL);
345
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);
351
352         frame->loader().client()->loadURLExternally(request, NavigationPolicyDownload, suggestedName);
353     } else {
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);
360     }
361 }
362
363 bool isEnterKeyKeypressEvent(Event* event)
364 {
365     return event->type() == EventTypeNames::keypress && event->isKeyboardEvent() && toKeyboardEvent(event)->keyIdentifier() == "Enter";
366 }
367
368 bool isLinkClick(Event* event)
369 {
370     return event->type() == EventTypeNames::click && (!event->isMouseEvent() || toMouseEvent(event)->button() != RightButton);
371 }
372
373 bool HTMLAnchorElement::willRespondToMouseClickEvents()
374 {
375     return isLink() || HTMLElement::willRespondToMouseClickEvents();
376 }
377
378 bool HTMLAnchorElement::isInteractiveContent() const
379 {
380     return isLink();
381 }
382
383 Node::InsertionNotificationRequest HTMLAnchorElement::insertedInto(ContainerNode* insertionPoint)
384 {
385     if (insertionPoint->inDocument()) {
386         V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
387         if (activityLogger) {
388             Vector<String> argv;
389             argv.append("a");
390             argv.append(fastGetAttribute(hrefAttr));
391             activityLogger->logEvent("blinkAddElement", argv.size(), argv.data());
392         }
393     }
394     return HTMLElement::insertedInto(insertionPoint);
395 }
396
397 }