- add third_party src.
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / HitTestResult.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20 */
21
22 #include "config.h"
23 #include "core/rendering/HitTestResult.h"
24
25 #include "HTMLNames.h"
26 #include "SVGNames.h"
27 #include "XLinkNames.h"
28 #include "core/dom/DocumentMarkerController.h"
29 #include "core/dom/NodeRenderingTraversal.h"
30 #include "core/dom/shadow/ShadowRoot.h"
31 #include "core/editing/FrameSelection.h"
32 #include "core/fetch/ImageResource.h"
33 #include "core/html/HTMLAnchorElement.h"
34 #include "core/html/HTMLAreaElement.h"
35 #include "core/html/HTMLImageElement.h"
36 #include "core/html/HTMLInputElement.h"
37 #include "core/html/HTMLMediaElement.h"
38 #include "core/html/HTMLTextAreaElement.h"
39 #include "core/html/HTMLVideoElement.h"
40 #include "core/html/parser/HTMLParserIdioms.h"
41 #include "core/frame/Frame.h"
42 #include "core/page/FrameTree.h"
43 #include "core/platform/Scrollbar.h"
44 #include "core/rendering/HitTestLocation.h"
45 #include "core/rendering/RenderImage.h"
46 #include "core/rendering/RenderTextFragment.h"
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 HitTestResult::HitTestResult()
53     : m_isOverWidget(false)
54     , m_isFirstLetter(false)
55 {
56 }
57
58 HitTestResult::HitTestResult(const LayoutPoint& point)
59     : m_hitTestLocation(point)
60     , m_pointInInnerNodeFrame(point)
61     , m_isOverWidget(false)
62     , m_isFirstLetter(false)
63 {
64 }
65
66 HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
67     : m_hitTestLocation(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding)
68     , m_pointInInnerNodeFrame(centerPoint)
69     , m_isOverWidget(false)
70     , m_isFirstLetter(false)
71 {
72 }
73
74 HitTestResult::HitTestResult(const HitTestLocation& other)
75     : m_hitTestLocation(other)
76     , m_pointInInnerNodeFrame(m_hitTestLocation.point())
77     , m_isOverWidget(false)
78     , m_isFirstLetter(false)
79 {
80 }
81
82 HitTestResult::HitTestResult(const HitTestResult& other)
83     : m_hitTestLocation(other.m_hitTestLocation)
84     , m_innerNode(other.innerNode())
85     , m_innerPossiblyPseudoNode(other.m_innerPossiblyPseudoNode)
86     , m_innerNonSharedNode(other.innerNonSharedNode())
87     , m_pointInInnerNodeFrame(other.m_pointInInnerNodeFrame)
88     , m_localPoint(other.localPoint())
89     , m_innerURLElement(other.URLElement())
90     , m_scrollbar(other.scrollbar())
91     , m_isOverWidget(other.isOverWidget())
92     , m_isFirstLetter(other.m_isFirstLetter)
93 {
94     // Only copy the NodeSet in case of rect hit test.
95     m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
96 }
97
98 HitTestResult::~HitTestResult()
99 {
100 }
101
102 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
103 {
104     m_hitTestLocation = other.m_hitTestLocation;
105     m_innerNode = other.innerNode();
106     m_innerPossiblyPseudoNode = other.innerPossiblyPseudoNode();
107     m_innerNonSharedNode = other.innerNonSharedNode();
108     m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
109     m_localPoint = other.localPoint();
110     m_innerURLElement = other.URLElement();
111     m_scrollbar = other.scrollbar();
112     m_isFirstLetter = other.m_isFirstLetter;
113     m_isOverWidget = other.isOverWidget();
114
115     // Only copy the NodeSet in case of rect hit test.
116     m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
117
118     return *this;
119 }
120
121 RenderObject* HitTestResult::renderer() const
122 {
123     if (!m_innerNode)
124         return 0;
125     RenderObject* renderer = m_innerNode->renderer();
126     if (!m_isFirstLetter || !renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragment())
127         return renderer;
128     return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter();
129 }
130
131 void HitTestResult::setToNodesInDocumentTreeScope()
132 {
133     if (Node* node = innerNode()) {
134         node = node->document().ancestorInThisScope(node);
135         setInnerNode(node);
136     }
137
138     if (Node* node = innerNonSharedNode()) {
139         node = node->document().ancestorInThisScope(node);
140         setInnerNonSharedNode(node);
141     }
142 }
143
144 void HitTestResult::setToShadowHostIfInUserAgentShadowRoot()
145 {
146     if (Node* node = innerNode()) {
147         if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
148             if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
149                 setInnerNode(node->shadowHost());
150         }
151     }
152
153     if (Node* node = innerNonSharedNode()) {
154         if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
155             if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
156                 setInnerNonSharedNode(node->shadowHost());
157         }
158     }
159 }
160
161 void HitTestResult::setInnerNode(Node* n)
162 {
163     m_innerPossiblyPseudoNode = n;
164     if (n && n->isPseudoElement())
165         n = n->parentOrShadowHostNode();
166     m_innerNode = n;
167 }
168
169 void HitTestResult::setInnerNonSharedNode(Node* n)
170 {
171     if (n && n->isPseudoElement())
172         n = n->parentOrShadowHostNode();
173     m_innerNonSharedNode = n;
174 }
175
176 void HitTestResult::setURLElement(Element* n)
177 {
178     m_innerURLElement = n;
179 }
180
181 void HitTestResult::setScrollbar(Scrollbar* s)
182 {
183     m_scrollbar = s;
184 }
185
186 Frame* HitTestResult::innerNodeFrame() const
187 {
188     if (m_innerNonSharedNode)
189         return m_innerNonSharedNode->document().frame();
190     if (m_innerNode)
191         return m_innerNode->document().frame();
192     return 0;
193 }
194
195 Frame* HitTestResult::targetFrame() const
196 {
197     if (!m_innerURLElement)
198         return 0;
199
200     Frame* frame = m_innerURLElement->document().frame();
201     if (!frame)
202         return 0;
203
204     return frame->tree().find(m_innerURLElement->target());
205 }
206
207 bool HitTestResult::isSelected() const
208 {
209     if (!m_innerNonSharedNode)
210         return false;
211
212     if (Frame* frame = m_innerNonSharedNode->document().frame())
213         return frame->selection().contains(m_hitTestLocation.point());
214     return false;
215 }
216
217 String HitTestResult::spellingToolTip(TextDirection& dir) const
218 {
219     dir = LTR;
220     // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
221     // currently supply strings, but maybe someday markers associated with misspelled words will also.
222     if (!m_innerNonSharedNode)
223         return String();
224
225     DocumentMarker* marker = m_innerNonSharedNode->document().markers()->markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Grammar);
226     if (!marker)
227         return String();
228
229     if (RenderObject* renderer = m_innerNonSharedNode->renderer())
230         dir = renderer->style()->direction();
231     return marker->description();
232 }
233
234 String HitTestResult::title(TextDirection& dir) const
235 {
236     dir = LTR;
237     // Find the title in the nearest enclosing DOM node.
238     // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
239     for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
240         if (titleNode->isElementNode()) {
241             String title = toElement(titleNode)->title();
242             if (!title.isEmpty()) {
243                 if (RenderObject* renderer = titleNode->renderer())
244                     dir = renderer->style()->direction();
245                 return title;
246             }
247         }
248     }
249     return String();
250 }
251
252 String HitTestResult::altDisplayString() const
253 {
254     if (!m_innerNonSharedNode)
255         return String();
256
257     if (m_innerNonSharedNode->hasTagName(imgTag)) {
258         HTMLImageElement* image = toHTMLImageElement(m_innerNonSharedNode);
259         return image->getAttribute(altAttr);
260     }
261
262     if (m_innerNonSharedNode->hasTagName(inputTag)) {
263         HTMLInputElement* input = toHTMLInputElement(m_innerNonSharedNode);
264         return input->alt();
265     }
266
267     return String();
268 }
269
270 Image* HitTestResult::image() const
271 {
272     if (!m_innerNonSharedNode)
273         return 0;
274
275     RenderObject* renderer = m_innerNonSharedNode->renderer();
276     if (renderer && renderer->isImage()) {
277         RenderImage* image = toRenderImage(renderer);
278         if (image->cachedImage() && !image->cachedImage()->errorOccurred())
279             return image->cachedImage()->imageForRenderer(image);
280     }
281
282     return 0;
283 }
284
285 IntRect HitTestResult::imageRect() const
286 {
287     if (!image())
288         return IntRect();
289     return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
290 }
291
292 KURL HitTestResult::absoluteImageURL() const
293 {
294     if (!m_innerNonSharedNode)
295         return KURL();
296
297     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
298         return KURL();
299
300     AtomicString urlString;
301     if (m_innerNonSharedNode->hasTagName(embedTag)
302         || m_innerNonSharedNode->hasTagName(imgTag)
303         || m_innerNonSharedNode->hasTagName(inputTag)
304         || m_innerNonSharedNode->hasTagName(objectTag)
305         || m_innerNonSharedNode->hasTagName(SVGNames::imageTag)
306        ) {
307         urlString = toElement(m_innerNonSharedNode)->imageSourceURL();
308     } else
309         return KURL();
310
311     return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
312 }
313
314 KURL HitTestResult::absoluteMediaURL() const
315 {
316     if (HTMLMediaElement* mediaElt = mediaElement())
317         return mediaElt->currentSrc();
318     return KURL();
319 }
320
321 HTMLMediaElement* HitTestResult::mediaElement() const
322 {
323     if (!m_innerNonSharedNode)
324         return 0;
325
326     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
327         return 0;
328
329     if (isHTMLVideoElement(m_innerNonSharedNode.get()) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag))
330         return toHTMLMediaElement(m_innerNonSharedNode.get());
331     return 0;
332 }
333
334 KURL HitTestResult::absoluteLinkURL() const
335 {
336     if (!m_innerURLElement)
337         return KURL();
338
339     AtomicString urlString;
340     if (isHTMLAnchorElement(m_innerURLElement.get()) || isHTMLAreaElement(m_innerURLElement.get()) || m_innerURLElement->hasTagName(linkTag))
341         urlString = m_innerURLElement->getAttribute(hrefAttr);
342     else if (m_innerURLElement->hasTagName(SVGNames::aTag))
343         urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr);
344     else
345         return KURL();
346
347     return m_innerURLElement->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
348 }
349
350 bool HitTestResult::isLiveLink() const
351 {
352     if (!m_innerURLElement)
353         return false;
354
355     if (isHTMLAnchorElement(m_innerURLElement.get()))
356         return toHTMLAnchorElement(m_innerURLElement)->isLiveLink();
357
358     if (m_innerURLElement->hasTagName(SVGNames::aTag))
359         return m_innerURLElement->isLink();
360
361     return false;
362 }
363
364 bool HitTestResult::isMisspelled() const
365 {
366     if (!targetNode())
367         return false;
368     VisiblePosition pos(targetNode()->renderer()->positionForPoint(localPoint()));
369     if (pos.isNull())
370         return false;
371     return m_innerNonSharedNode->document().markers()->markersInRange(
372         makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers()).size() > 0;
373 }
374
375 bool HitTestResult::isOverLink() const
376 {
377     return m_innerURLElement && m_innerURLElement->isLink();
378 }
379
380 String HitTestResult::titleDisplayString() const
381 {
382     if (!m_innerURLElement)
383         return String();
384
385     return m_innerURLElement->title();
386 }
387
388 String HitTestResult::textContent() const
389 {
390     if (!m_innerURLElement)
391         return String();
392     return m_innerURLElement->textContent();
393 }
394
395 // FIXME: This function needs a better name and may belong in a different class. It's not
396 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
397 // function would make more sense in the ContextMenu class, except that WebElementDictionary
398 // hooks into it. Anyway, we should architect this better.
399 bool HitTestResult::isContentEditable() const
400 {
401     if (!m_innerNonSharedNode)
402         return false;
403
404     if (isHTMLTextAreaElement(m_innerNonSharedNode.get()))
405         return true;
406
407     if (m_innerNonSharedNode->hasTagName(inputTag))
408         return toHTMLInputElement(m_innerNonSharedNode)->isTextField();
409
410     return m_innerNonSharedNode->rendererIsEditable();
411 }
412
413 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
414 {
415     // If it is not a rect-based hit test, this method has to be no-op.
416     // Return false, so the hit test stops.
417     if (!isRectBasedTest())
418         return false;
419
420     // If node is null, return true so the hit test can continue.
421     if (!node)
422         return true;
423
424     if (request.disallowsShadowContent())
425         node = node->document().ancestorInThisScope(node);
426
427     mutableRectBasedTestResult().add(node);
428
429     bool regionFilled = rect.contains(locationInContainer.boundingBox());
430     return !regionFilled;
431 }
432
433 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
434 {
435     // If it is not a rect-based hit test, this method has to be no-op.
436     // Return false, so the hit test stops.
437     if (!isRectBasedTest())
438         return false;
439
440     // If node is null, return true so the hit test can continue.
441     if (!node)
442         return true;
443
444     if (request.disallowsShadowContent())
445         node = node->document().ancestorInThisScope(node);
446
447     mutableRectBasedTestResult().add(node);
448
449     bool regionFilled = rect.contains(locationInContainer.boundingBox());
450     return !regionFilled;
451 }
452
453 void HitTestResult::append(const HitTestResult& other)
454 {
455     ASSERT(isRectBasedTest() && other.isRectBasedTest());
456
457     if (!m_innerNode && other.innerNode()) {
458         m_innerNode = other.innerNode();
459         m_innerPossiblyPseudoNode = other.innerPossiblyPseudoNode();
460         m_innerNonSharedNode = other.innerNonSharedNode();
461         m_localPoint = other.localPoint();
462         m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
463         m_innerURLElement = other.URLElement();
464         m_scrollbar = other.scrollbar();
465         m_isOverWidget = other.isOverWidget();
466     }
467
468     if (other.m_rectBasedTestResult) {
469         NodeSet& set = mutableRectBasedTestResult();
470         for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
471             set.add(it->get());
472     }
473 }
474
475 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
476 {
477     if (!m_rectBasedTestResult)
478         m_rectBasedTestResult = adoptPtr(new NodeSet);
479     return *m_rectBasedTestResult;
480 }
481
482 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
483 {
484     if (!m_rectBasedTestResult)
485         m_rectBasedTestResult = adoptPtr(new NodeSet);
486     return *m_rectBasedTestResult;
487 }
488
489 Node* HitTestResult::targetNode() const
490 {
491     Node* node = innerNode();
492     if (!node)
493         return 0;
494     if (node->inDocument())
495         return node;
496
497     Element* element = node->parentElement();
498     if (element && element->inDocument())
499         return element;
500
501     return node;
502 }
503
504 Element* HitTestResult::innerElement() const
505 {
506     for (Node* node = m_innerNode.get(); node; node = NodeRenderingTraversal::parent(node)) {
507         if (node->isElementNode())
508             return toElement(node);
509     }
510
511     return 0;
512 }
513
514 } // namespace WebCore