Update To 11.40.268.0
[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 "core/HTMLNames.h"
26 #include "core/dom/DocumentMarkerController.h"
27 #include "core/dom/NodeRenderingTraversal.h"
28 #include "core/dom/shadow/ShadowRoot.h"
29 #include "core/editing/FrameSelection.h"
30 #include "core/fetch/ImageResource.h"
31 #include "core/frame/LocalFrame.h"
32 #include "core/html/HTMLAnchorElement.h"
33 #include "core/html/HTMLImageElement.h"
34 #include "core/html/HTMLInputElement.h"
35 #include "core/html/HTMLMediaElement.h"
36 #include "core/html/HTMLTextAreaElement.h"
37 #include "core/html/parser/HTMLParserIdioms.h"
38 #include "core/page/FrameTree.h"
39 #include "core/rendering/RenderImage.h"
40 #include "core/rendering/RenderTextFragment.h"
41 #include "core/svg/SVGElement.h"
42 #include "platform/scroll/Scrollbar.h"
43
44 namespace blink {
45
46 using namespace HTMLNames;
47
48 HitTestResult::HitTestResult()
49     : m_isOverWidget(false)
50     , m_isFirstLetter(false)
51 {
52 }
53
54 HitTestResult::HitTestResult(const LayoutPoint& point)
55     : m_hitTestLocation(point)
56     , m_pointInInnerNodeFrame(point)
57     , m_isOverWidget(false)
58     , m_isFirstLetter(false)
59 {
60 }
61
62 HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
63     : m_hitTestLocation(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding)
64     , m_pointInInnerNodeFrame(centerPoint)
65     , m_isOverWidget(false)
66     , m_isFirstLetter(false)
67 {
68 }
69
70 HitTestResult::HitTestResult(const HitTestLocation& other)
71     : m_hitTestLocation(other)
72     , m_pointInInnerNodeFrame(m_hitTestLocation.point())
73     , m_isOverWidget(false)
74     , m_isFirstLetter(false)
75 {
76 }
77
78 HitTestResult::HitTestResult(const HitTestResult& other)
79     : m_hitTestLocation(other.m_hitTestLocation)
80     , m_innerNode(other.innerNode())
81     , m_innerPossiblyPseudoNode(other.m_innerPossiblyPseudoNode)
82     , m_innerNonSharedNode(other.innerNonSharedNode())
83     , m_pointInInnerNodeFrame(other.m_pointInInnerNodeFrame)
84     , m_localPoint(other.localPoint())
85     , m_innerURLElement(other.URLElement())
86     , m_scrollbar(other.scrollbar())
87     , m_isOverWidget(other.isOverWidget())
88     , m_isFirstLetter(other.m_isFirstLetter)
89 {
90     // Only copy the NodeSet in case of rect hit test.
91     m_rectBasedTestResult = adoptPtrWillBeNoop(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
92 }
93
94 HitTestResult::~HitTestResult()
95 {
96 }
97
98 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
99 {
100     m_hitTestLocation = other.m_hitTestLocation;
101     m_innerNode = other.innerNode();
102     m_innerPossiblyPseudoNode = other.innerPossiblyPseudoNode();
103     m_innerNonSharedNode = other.innerNonSharedNode();
104     m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
105     m_localPoint = other.localPoint();
106     m_innerURLElement = other.URLElement();
107     m_scrollbar = other.scrollbar();
108     m_isFirstLetter = other.m_isFirstLetter;
109     m_isOverWidget = other.isOverWidget();
110
111     // Only copy the NodeSet in case of rect hit test.
112     m_rectBasedTestResult = adoptPtrWillBeNoop(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
113
114     return *this;
115 }
116
117 void HitTestResult::trace(Visitor* visitor)
118 {
119     visitor->trace(m_innerNode);
120     visitor->trace(m_innerPossiblyPseudoNode);
121     visitor->trace(m_innerNonSharedNode);
122     visitor->trace(m_innerURLElement);
123     visitor->trace(m_scrollbar);
124 #if ENABLE(OILPAN)
125     visitor->trace(m_rectBasedTestResult);
126 #endif
127 }
128
129 PositionWithAffinity HitTestResult::position() const
130 {
131     if (!m_innerPossiblyPseudoNode)
132         return PositionWithAffinity();
133     RenderObject* renderer = this->renderer();
134     if (!renderer)
135         return PositionWithAffinity();
136     if (m_innerPossiblyPseudoNode->isPseudoElement() && m_innerPossiblyPseudoNode->pseudoId() == BEFORE)
137         return Position(m_innerNode, Position::PositionIsBeforeChildren).downstream();
138     return renderer->positionForPoint(localPoint());
139 }
140
141 RenderObject* HitTestResult::renderer() const
142 {
143     if (!m_innerNode)
144         return 0;
145     RenderObject* renderer = m_innerNode->renderer();
146     if (!m_isFirstLetter || !renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragment())
147         return renderer;
148     return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter();
149 }
150
151 void HitTestResult::setToShadowHostIfInUserAgentShadowRoot()
152 {
153     if (Node* node = innerNode()) {
154         if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
155             if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
156                 setInnerNode(node->shadowHost());
157         }
158     }
159
160     if (Node* node = innerNonSharedNode()) {
161         if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
162             if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
163                 setInnerNonSharedNode(node->shadowHost());
164         }
165     }
166 }
167
168 void HitTestResult::setInnerNode(Node* n)
169 {
170     m_innerPossiblyPseudoNode = n;
171     if (n && n->isPseudoElement())
172         n = n->parentOrShadowHostNode();
173     m_innerNode = n;
174 }
175
176 void HitTestResult::setInnerNonSharedNode(Node* n)
177 {
178     if (n && n->isPseudoElement())
179         n = n->parentOrShadowHostNode();
180     m_innerNonSharedNode = n;
181 }
182
183 void HitTestResult::setURLElement(Element* n)
184 {
185     m_innerURLElement = n;
186 }
187
188 void HitTestResult::setScrollbar(Scrollbar* s)
189 {
190     m_scrollbar = s;
191 }
192
193 LocalFrame* HitTestResult::innerNodeFrame() const
194 {
195     if (m_innerNonSharedNode)
196         return m_innerNonSharedNode->document().frame();
197     if (m_innerNode)
198         return m_innerNode->document().frame();
199     return 0;
200 }
201
202 bool HitTestResult::isSelected() const
203 {
204     if (!m_innerNonSharedNode)
205         return false;
206
207     if (LocalFrame* frame = m_innerNonSharedNode->document().frame())
208         return frame->selection().contains(m_hitTestLocation.point());
209     return false;
210 }
211
212 String HitTestResult::spellingToolTip(TextDirection& dir) const
213 {
214     dir = LTR;
215     // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
216     // currently supply strings, but maybe someday markers associated with misspelled words will also.
217     if (!m_innerNonSharedNode)
218         return String();
219
220     DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Grammar);
221     if (!marker)
222         return String();
223
224     if (RenderObject* renderer = m_innerNonSharedNode->renderer())
225         dir = renderer->style()->direction();
226     return marker->description();
227 }
228
229 String HitTestResult::title(TextDirection& dir) const
230 {
231     dir = LTR;
232     // Find the title in the nearest enclosing DOM node.
233     // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
234     for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
235         if (titleNode->isElementNode()) {
236             String title = toElement(titleNode)->title();
237             if (!title.isNull()) {
238                 if (RenderObject* renderer = titleNode->renderer())
239                     dir = renderer->style()->direction();
240                 return title;
241             }
242         }
243     }
244     return String();
245 }
246
247 const AtomicString& HitTestResult::altDisplayString() const
248 {
249     if (!m_innerNonSharedNode)
250         return nullAtom;
251
252     if (isHTMLImageElement(*m_innerNonSharedNode)) {
253         HTMLImageElement& image = toHTMLImageElement(*m_innerNonSharedNode);
254         return image.getAttribute(altAttr);
255     }
256
257     if (isHTMLInputElement(*m_innerNonSharedNode)) {
258         HTMLInputElement& input = toHTMLInputElement(*m_innerNonSharedNode);
259         return input.alt();
260     }
261
262     return nullAtom;
263 }
264
265 Image* HitTestResult::image() const
266 {
267     if (!m_innerNonSharedNode)
268         return 0;
269
270     RenderObject* renderer = m_innerNonSharedNode->renderer();
271     if (renderer && renderer->isImage()) {
272         RenderImage* image = toRenderImage(renderer);
273         if (image->cachedImage() && !image->cachedImage()->errorOccurred())
274             return image->cachedImage()->imageForRenderer(image);
275     }
276
277     return 0;
278 }
279
280 IntRect HitTestResult::imageRect() const
281 {
282     if (!image())
283         return IntRect();
284     return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
285 }
286
287 KURL HitTestResult::absoluteImageURL() const
288 {
289     if (!m_innerNonSharedNode)
290         return KURL();
291
292     RenderObject* renderer = m_innerNonSharedNode->renderer();
293     if (!(renderer && renderer->isImage()))
294         return KURL();
295
296     AtomicString urlString;
297     if (isHTMLEmbedElement(*m_innerNonSharedNode)
298         || isHTMLImageElement(*m_innerNonSharedNode)
299         || isHTMLInputElement(*m_innerNonSharedNode)
300         || isHTMLObjectElement(*m_innerNonSharedNode)
301         || isSVGImageElement(*m_innerNonSharedNode)
302        ) {
303         urlString = toElement(*m_innerNonSharedNode).imageSourceURL();
304     } else
305         return KURL();
306
307     return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
308 }
309
310 KURL HitTestResult::absoluteMediaURL() const
311 {
312     if (HTMLMediaElement* mediaElt = mediaElement())
313         return mediaElt->currentSrc();
314     return KURL();
315 }
316
317 HTMLMediaElement* HitTestResult::mediaElement() const
318 {
319     if (!m_innerNonSharedNode)
320         return 0;
321
322     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
323         return 0;
324
325     if (isHTMLMediaElement(*m_innerNonSharedNode))
326         return toHTMLMediaElement(m_innerNonSharedNode);
327     return 0;
328 }
329
330 KURL HitTestResult::absoluteLinkURL() const
331 {
332     if (!m_innerURLElement)
333         return KURL();
334     return m_innerURLElement->hrefURL();
335 }
336
337 bool HitTestResult::isLiveLink() const
338 {
339     return m_innerURLElement && m_innerURLElement->isLiveLink();
340 }
341
342 bool HitTestResult::isMisspelled() const
343 {
344     if (!innerNode() || !innerNode()->renderer())
345         return false;
346     VisiblePosition pos(innerNode()->renderer()->positionForPoint(localPoint()));
347     if (pos.isNull())
348         return false;
349     return m_innerNonSharedNode->document().markers().markersInRange(
350         makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers()).size() > 0;
351 }
352
353 bool HitTestResult::isOverLink() const
354 {
355     return m_innerURLElement && m_innerURLElement->isLink();
356 }
357
358 String HitTestResult::textContent() const
359 {
360     if (!m_innerURLElement)
361         return String();
362     return m_innerURLElement->textContent();
363 }
364
365 // FIXME: This function needs a better name and may belong in a different class. It's not
366 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
367 // function would make more sense in the ContextMenu class, except that WebElementDictionary
368 // hooks into it. Anyway, we should architect this better.
369 bool HitTestResult::isContentEditable() const
370 {
371     if (!m_innerNonSharedNode)
372         return false;
373
374     if (isHTMLTextAreaElement(*m_innerNonSharedNode))
375         return !toHTMLTextAreaElement(*m_innerNonSharedNode).isDisabledOrReadOnly();
376
377     if (isHTMLInputElement(*m_innerNonSharedNode)) {
378         HTMLInputElement& inputElement = toHTMLInputElement(*m_innerNonSharedNode);
379         return !inputElement.isDisabledOrReadOnly() && inputElement.isTextField();
380     }
381
382     return m_innerNonSharedNode->hasEditableStyle();
383 }
384
385 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
386 {
387     // If it is not a rect-based hit test, this method has to be no-op.
388     // Return false, so the hit test stops.
389     if (!isRectBasedTest())
390         return false;
391
392     // If node is null, return true so the hit test can continue.
393     if (!node)
394         return true;
395
396     mutableRectBasedTestResult().add(node);
397
398     bool regionFilled = rect.contains(locationInContainer.boundingBox());
399     return !regionFilled;
400 }
401
402 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
403 {
404     // If it is not a rect-based hit test, this method has to be no-op.
405     // Return false, so the hit test stops.
406     if (!isRectBasedTest())
407         return false;
408
409     // If node is null, return true so the hit test can continue.
410     if (!node)
411         return true;
412
413     mutableRectBasedTestResult().add(node);
414
415     bool regionFilled = rect.contains(locationInContainer.boundingBox());
416     return !regionFilled;
417 }
418
419 void HitTestResult::append(const HitTestResult& other)
420 {
421     ASSERT(isRectBasedTest() && other.isRectBasedTest());
422
423     if (!m_scrollbar && other.scrollbar()) {
424         setScrollbar(other.scrollbar());
425     }
426
427     if (!m_innerNode && other.innerNode()) {
428         m_innerNode = other.innerNode();
429         m_innerPossiblyPseudoNode = other.innerPossiblyPseudoNode();
430         m_innerNonSharedNode = other.innerNonSharedNode();
431         m_localPoint = other.localPoint();
432         m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
433         m_innerURLElement = other.URLElement();
434         m_isOverWidget = other.isOverWidget();
435     }
436
437     if (other.m_rectBasedTestResult) {
438         NodeSet& set = mutableRectBasedTestResult();
439         for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
440             set.add(it->get());
441     }
442 }
443
444 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
445 {
446     if (!m_rectBasedTestResult)
447         m_rectBasedTestResult = adoptPtrWillBeNoop(new NodeSet);
448     return *m_rectBasedTestResult;
449 }
450
451 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
452 {
453     if (!m_rectBasedTestResult)
454         m_rectBasedTestResult = adoptPtrWillBeNoop(new NodeSet);
455     return *m_rectBasedTestResult;
456 }
457
458 void HitTestResult::resolveRectBasedTest(Node* resolvedInnerNode, const LayoutPoint& resolvedPointInMainFrame)
459 {
460     ASSERT(isRectBasedTest());
461     ASSERT(m_hitTestLocation.containsPoint(resolvedPointInMainFrame));
462     m_hitTestLocation = HitTestLocation(resolvedPointInMainFrame);
463     m_pointInInnerNodeFrame = resolvedPointInMainFrame;
464     m_innerNode = nullptr;
465     m_innerNonSharedNode = nullptr;
466     m_innerPossiblyPseudoNode = nullptr;
467     m_rectBasedTestResult = nullptr;
468
469     // Update the HitTestResult as if the supplied node had been hit in normal point-based hit-test.
470     // Note that we don't know the local point after a rect-based hit-test, but we never use
471     // it so shouldn't bother with the cost of computing it.
472     resolvedInnerNode->renderer()->updateHitTestResult(*this, LayoutPoint());
473     ASSERT(!isRectBasedTest());
474 }
475
476 Element* HitTestResult::innerElement() const
477 {
478     for (Node* node = m_innerNode.get(); node; node = NodeRenderingTraversal::parent(node)) {
479         if (node->isElementNode())
480             return toElement(node);
481     }
482
483     return 0;
484 }
485
486 } // namespace blink