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 "HTMLNames.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"
50 using namespace HTMLNames;
52 HitTestResult::HitTestResult()
53 : m_isOverWidget(false)
54 , m_isFirstLetter(false)
58 HitTestResult::HitTestResult(const LayoutPoint& point)
59 : m_hitTestLocation(point)
60 , m_pointInInnerNodeFrame(point)
61 , m_isOverWidget(false)
62 , m_isFirstLetter(false)
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)
74 HitTestResult::HitTestResult(const HitTestLocation& other)
75 : m_hitTestLocation(other)
76 , m_pointInInnerNodeFrame(m_hitTestLocation.point())
77 , m_isOverWidget(false)
78 , m_isFirstLetter(false)
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)
94 // Only copy the NodeSet in case of rect hit test.
95 m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
98 HitTestResult::~HitTestResult()
102 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
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();
115 // Only copy the NodeSet in case of rect hit test.
116 m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
121 RenderObject* HitTestResult::renderer() const
125 RenderObject* renderer = m_innerNode->renderer();
126 if (!m_isFirstLetter || !renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragment())
128 return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter();
131 void HitTestResult::setToNodesInDocumentTreeScope()
133 if (Node* node = innerNode()) {
134 node = node->document().ancestorInThisScope(node);
138 if (Node* node = innerNonSharedNode()) {
139 node = node->document().ancestorInThisScope(node);
140 setInnerNonSharedNode(node);
144 void HitTestResult::setToShadowHostIfInUserAgentShadowRoot()
146 if (Node* node = innerNode()) {
147 if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
148 if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
149 setInnerNode(node->shadowHost());
153 if (Node* node = innerNonSharedNode()) {
154 if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
155 if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
156 setInnerNonSharedNode(node->shadowHost());
161 void HitTestResult::setInnerNode(Node* n)
163 m_innerPossiblyPseudoNode = n;
164 if (n && n->isPseudoElement())
165 n = n->parentOrShadowHostNode();
169 void HitTestResult::setInnerNonSharedNode(Node* n)
171 if (n && n->isPseudoElement())
172 n = n->parentOrShadowHostNode();
173 m_innerNonSharedNode = n;
176 void HitTestResult::setURLElement(Element* n)
178 m_innerURLElement = n;
181 void HitTestResult::setScrollbar(Scrollbar* s)
186 Frame* HitTestResult::innerNodeFrame() const
188 if (m_innerNonSharedNode)
189 return m_innerNonSharedNode->document().frame();
191 return m_innerNode->document().frame();
195 Frame* HitTestResult::targetFrame() const
197 if (!m_innerURLElement)
200 Frame* frame = m_innerURLElement->document().frame();
204 return frame->tree().find(m_innerURLElement->target());
207 bool HitTestResult::isSelected() const
209 if (!m_innerNonSharedNode)
212 if (Frame* frame = m_innerNonSharedNode->document().frame())
213 return frame->selection().contains(m_hitTestLocation.point());
217 String HitTestResult::spellingToolTip(TextDirection& dir) const
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)
225 DocumentMarker* marker = m_innerNonSharedNode->document().markers()->markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Grammar);
229 if (RenderObject* renderer = m_innerNonSharedNode->renderer())
230 dir = renderer->style()->direction();
231 return marker->description();
234 String HitTestResult::title(TextDirection& dir) const
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();
252 String HitTestResult::altDisplayString() const
254 if (!m_innerNonSharedNode)
257 if (m_innerNonSharedNode->hasTagName(imgTag)) {
258 HTMLImageElement* image = toHTMLImageElement(m_innerNonSharedNode);
259 return image->getAttribute(altAttr);
262 if (m_innerNonSharedNode->hasTagName(inputTag)) {
263 HTMLInputElement* input = toHTMLInputElement(m_innerNonSharedNode);
270 Image* HitTestResult::image() const
272 if (!m_innerNonSharedNode)
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);
285 IntRect HitTestResult::imageRect() const
289 return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
292 KURL HitTestResult::absoluteImageURL() const
294 if (!m_innerNonSharedNode)
297 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
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)
307 urlString = toElement(m_innerNonSharedNode)->imageSourceURL();
311 return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
314 KURL HitTestResult::absoluteMediaURL() const
316 if (HTMLMediaElement* mediaElt = mediaElement())
317 return mediaElt->currentSrc();
321 HTMLMediaElement* HitTestResult::mediaElement() const
323 if (!m_innerNonSharedNode)
326 if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
329 if (isHTMLVideoElement(m_innerNonSharedNode.get()) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag))
330 return toHTMLMediaElement(m_innerNonSharedNode.get());
334 KURL HitTestResult::absoluteLinkURL() const
336 if (!m_innerURLElement)
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);
347 return m_innerURLElement->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
350 bool HitTestResult::isLiveLink() const
352 if (!m_innerURLElement)
355 if (isHTMLAnchorElement(m_innerURLElement.get()))
356 return toHTMLAnchorElement(m_innerURLElement)->isLiveLink();
358 if (m_innerURLElement->hasTagName(SVGNames::aTag))
359 return m_innerURLElement->isLink();
364 bool HitTestResult::isMisspelled() const
368 VisiblePosition pos(targetNode()->renderer()->positionForPoint(localPoint()));
371 return m_innerNonSharedNode->document().markers()->markersInRange(
372 makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers()).size() > 0;
375 bool HitTestResult::isOverLink() const
377 return m_innerURLElement && m_innerURLElement->isLink();
380 String HitTestResult::titleDisplayString() const
382 if (!m_innerURLElement)
385 return m_innerURLElement->title();
388 String HitTestResult::textContent() const
390 if (!m_innerURLElement)
392 return m_innerURLElement->textContent();
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
401 if (!m_innerNonSharedNode)
404 if (isHTMLTextAreaElement(m_innerNonSharedNode.get()))
407 if (m_innerNonSharedNode->hasTagName(inputTag))
408 return toHTMLInputElement(m_innerNonSharedNode)->isTextField();
410 return m_innerNonSharedNode->rendererIsEditable();
413 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
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())
420 // If node is null, return true so the hit test can continue.
424 if (request.disallowsShadowContent())
425 node = node->document().ancestorInThisScope(node);
427 mutableRectBasedTestResult().add(node);
429 bool regionFilled = rect.contains(locationInContainer.boundingBox());
430 return !regionFilled;
433 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
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())
440 // If node is null, return true so the hit test can continue.
444 if (request.disallowsShadowContent())
445 node = node->document().ancestorInThisScope(node);
447 mutableRectBasedTestResult().add(node);
449 bool regionFilled = rect.contains(locationInContainer.boundingBox());
450 return !regionFilled;
453 void HitTestResult::append(const HitTestResult& other)
455 ASSERT(isRectBasedTest() && other.isRectBasedTest());
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();
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)
475 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
477 if (!m_rectBasedTestResult)
478 m_rectBasedTestResult = adoptPtr(new NodeSet);
479 return *m_rectBasedTestResult;
482 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
484 if (!m_rectBasedTestResult)
485 m_rectBasedTestResult = adoptPtr(new NodeSet);
486 return *m_rectBasedTestResult;
489 Node* HitTestResult::targetNode() const
491 Node* node = innerNode();
494 if (node->inDocument())
497 Element* element = node->parentElement();
498 if (element && element->inDocument())
504 Element* HitTestResult::innerElement() const
506 for (Node* node = m_innerNode.get(); node; node = NodeRenderingTraversal::parent(node)) {
507 if (node->isElementNode())
508 return toElement(node);
514 } // namespace WebCore