2 * Copyright (C) 2006, 2008, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
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.
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.
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.
23 #include "core/rendering/HitTestResult.h"
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"
46 using namespace HTMLNames;
48 HitTestResult::HitTestResult()
49 : m_isOverWidget(false)
50 , m_isFirstLetter(false)
54 HitTestResult::HitTestResult(const LayoutPoint& point)
55 : m_hitTestLocation(point)
56 , m_pointInInnerNodeFrame(point)
57 , m_isOverWidget(false)
58 , m_isFirstLetter(false)
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)
70 HitTestResult::HitTestResult(const HitTestLocation& other)
71 : m_hitTestLocation(other)
72 , m_pointInInnerNodeFrame(m_hitTestLocation.point())
73 , m_isOverWidget(false)
74 , m_isFirstLetter(false)
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)
90 // Only copy the NodeSet in case of rect hit test.
91 m_rectBasedTestResult = adoptPtrWillBeNoop(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
94 HitTestResult::~HitTestResult()
98 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
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();
111 // Only copy the NodeSet in case of rect hit test.
112 m_rectBasedTestResult = adoptPtrWillBeNoop(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
117 void HitTestResult::trace(Visitor* visitor)
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);
125 visitor->trace(m_rectBasedTestResult);
129 PositionWithAffinity HitTestResult::position() const
131 if (!m_innerPossiblyPseudoNode)
132 return PositionWithAffinity();
133 RenderObject* renderer = this->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());
141 RenderObject* HitTestResult::renderer() const
145 RenderObject* renderer = m_innerNode->renderer();
146 if (!m_isFirstLetter || !renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragment())
148 return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter();
151 void HitTestResult::setToShadowHostIfInUserAgentShadowRoot()
153 if (Node* node = innerNode()) {
154 if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
155 if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
156 setInnerNode(node->shadowHost());
160 if (Node* node = innerNonSharedNode()) {
161 if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
162 if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
163 setInnerNonSharedNode(node->shadowHost());
168 void HitTestResult::setInnerNode(Node* n)
170 m_innerPossiblyPseudoNode = n;
171 if (n && n->isPseudoElement())
172 n = n->parentOrShadowHostNode();
176 void HitTestResult::setInnerNonSharedNode(Node* n)
178 if (n && n->isPseudoElement())
179 n = n->parentOrShadowHostNode();
180 m_innerNonSharedNode = n;
183 void HitTestResult::setURLElement(Element* n)
185 m_innerURLElement = n;
188 void HitTestResult::setScrollbar(Scrollbar* s)
193 LocalFrame* HitTestResult::innerNodeFrame() const
195 if (m_innerNonSharedNode)
196 return m_innerNonSharedNode->document().frame();
198 return m_innerNode->document().frame();
202 bool HitTestResult::isSelected() const
204 if (!m_innerNonSharedNode)
207 if (LocalFrame* frame = m_innerNonSharedNode->document().frame())
208 return frame->selection().contains(m_hitTestLocation.point());
212 String HitTestResult::spellingToolTip(TextDirection& dir) const
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)
220 DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Grammar);
224 if (RenderObject* renderer = m_innerNonSharedNode->renderer())
225 dir = renderer->style()->direction();
226 return marker->description();
229 String HitTestResult::title(TextDirection& dir) const
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();
247 const AtomicString& HitTestResult::altDisplayString() const
249 if (!m_innerNonSharedNode)
252 if (isHTMLImageElement(*m_innerNonSharedNode)) {
253 HTMLImageElement& image = toHTMLImageElement(*m_innerNonSharedNode);
254 return image.getAttribute(altAttr);
257 if (isHTMLInputElement(*m_innerNonSharedNode)) {
258 HTMLInputElement& input = toHTMLInputElement(*m_innerNonSharedNode);
265 Image* HitTestResult::image() const
267 if (!m_innerNonSharedNode)
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);
280 IntRect HitTestResult::imageRect() const
284 return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
287 KURL HitTestResult::absoluteImageURL() const
289 if (!m_innerNonSharedNode)
292 RenderObject* renderer = m_innerNonSharedNode->renderer();
293 if (!(renderer && renderer->isImage()))
296 AtomicString urlString;
297 if (isHTMLEmbedElement(*m_innerNonSharedNode)
298 || isHTMLImageElement(*m_innerNonSharedNode)
299 || isHTMLInputElement(*m_innerNonSharedNode)
300 || isHTMLObjectElement(*m_innerNonSharedNode)
301 || isSVGImageElement(*m_innerNonSharedNode)
303 urlString = toElement(*m_innerNonSharedNode).imageSourceURL();
307 return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
310 KURL HitTestResult::absoluteMediaURL() const
312 if (HTMLMediaElement* mediaElt = mediaElement())
313 return mediaElt->currentSrc();
317 HTMLMediaElement* HitTestResult::mediaElement() const
319 if (!m_innerNonSharedNode)
322 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
325 if (isHTMLMediaElement(*m_innerNonSharedNode))
326 return toHTMLMediaElement(m_innerNonSharedNode);
330 KURL HitTestResult::absoluteLinkURL() const
332 if (!m_innerURLElement)
334 return m_innerURLElement->hrefURL();
337 bool HitTestResult::isLiveLink() const
339 return m_innerURLElement && m_innerURLElement->isLiveLink();
342 bool HitTestResult::isMisspelled() const
344 if (!innerNode() || !innerNode()->renderer())
346 VisiblePosition pos(innerNode()->renderer()->positionForPoint(localPoint()));
349 return m_innerNonSharedNode->document().markers().markersInRange(
350 makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers()).size() > 0;
353 bool HitTestResult::isOverLink() const
355 return m_innerURLElement && m_innerURLElement->isLink();
358 String HitTestResult::textContent() const
360 if (!m_innerURLElement)
362 return m_innerURLElement->textContent();
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
371 if (!m_innerNonSharedNode)
374 if (isHTMLTextAreaElement(*m_innerNonSharedNode))
375 return !toHTMLTextAreaElement(*m_innerNonSharedNode).isDisabledOrReadOnly();
377 if (isHTMLInputElement(*m_innerNonSharedNode)) {
378 HTMLInputElement& inputElement = toHTMLInputElement(*m_innerNonSharedNode);
379 return !inputElement.isDisabledOrReadOnly() && inputElement.isTextField();
382 return m_innerNonSharedNode->hasEditableStyle();
385 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
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())
392 // If node is null, return true so the hit test can continue.
396 mutableRectBasedTestResult().add(node);
398 bool regionFilled = rect.contains(locationInContainer.boundingBox());
399 return !regionFilled;
402 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
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())
409 // If node is null, return true so the hit test can continue.
413 mutableRectBasedTestResult().add(node);
415 bool regionFilled = rect.contains(locationInContainer.boundingBox());
416 return !regionFilled;
419 void HitTestResult::append(const HitTestResult& other)
421 ASSERT(isRectBasedTest() && other.isRectBasedTest());
423 if (!m_scrollbar && other.scrollbar()) {
424 setScrollbar(other.scrollbar());
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();
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)
444 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
446 if (!m_rectBasedTestResult)
447 m_rectBasedTestResult = adoptPtrWillBeNoop(new NodeSet);
448 return *m_rectBasedTestResult;
451 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
453 if (!m_rectBasedTestResult)
454 m_rectBasedTestResult = adoptPtrWillBeNoop(new NodeSet);
455 return *m_rectBasedTestResult;
458 void HitTestResult::resolveRectBasedTest(Node* resolvedInnerNode, const LayoutPoint& resolvedPointInMainFrame)
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;
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());
476 Element* HitTestResult::innerElement() const
478 for (Node* node = m_innerNode.get(); node; node = NodeRenderingTraversal::parent(node)) {
479 if (node->isElementNode())
480 return toElement(node);