• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/rendering/RenderImage.h"
44 #include "core/rendering/RenderTextFragment.h"
45 #include "platform/scroll/Scrollbar.h"
46 
47 namespace WebCore {
48 
49 using namespace HTMLNames;
50 
HitTestResult()51 HitTestResult::HitTestResult()
52     : m_isOverWidget(false)
53     , m_isFirstLetter(false)
54 {
55 }
56 
HitTestResult(const LayoutPoint & point)57 HitTestResult::HitTestResult(const LayoutPoint& point)
58     : m_hitTestLocation(point)
59     , m_pointInInnerNodeFrame(point)
60     , m_isOverWidget(false)
61     , m_isFirstLetter(false)
62 {
63 }
64 
HitTestResult(const LayoutPoint & centerPoint,unsigned topPadding,unsigned rightPadding,unsigned bottomPadding,unsigned leftPadding)65 HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
66     : m_hitTestLocation(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding)
67     , m_pointInInnerNodeFrame(centerPoint)
68     , m_isOverWidget(false)
69     , m_isFirstLetter(false)
70 {
71 }
72 
HitTestResult(const HitTestLocation & other)73 HitTestResult::HitTestResult(const HitTestLocation& other)
74     : m_hitTestLocation(other)
75     , m_pointInInnerNodeFrame(m_hitTestLocation.point())
76     , m_isOverWidget(false)
77     , m_isFirstLetter(false)
78 {
79 }
80 
HitTestResult(const HitTestResult & other)81 HitTestResult::HitTestResult(const HitTestResult& other)
82     : m_hitTestLocation(other.m_hitTestLocation)
83     , m_innerNode(other.innerNode())
84     , m_innerPossiblyPseudoNode(other.m_innerPossiblyPseudoNode)
85     , m_innerNonSharedNode(other.innerNonSharedNode())
86     , m_pointInInnerNodeFrame(other.m_pointInInnerNodeFrame)
87     , m_localPoint(other.localPoint())
88     , m_innerURLElement(other.URLElement())
89     , m_scrollbar(other.scrollbar())
90     , m_isOverWidget(other.isOverWidget())
91     , m_isFirstLetter(other.m_isFirstLetter)
92 {
93     // Only copy the NodeSet in case of rect hit test.
94     m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
95 }
96 
~HitTestResult()97 HitTestResult::~HitTestResult()
98 {
99 }
100 
operator =(const HitTestResult & other)101 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
102 {
103     m_hitTestLocation = other.m_hitTestLocation;
104     m_innerNode = other.innerNode();
105     m_innerPossiblyPseudoNode = other.innerPossiblyPseudoNode();
106     m_innerNonSharedNode = other.innerNonSharedNode();
107     m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
108     m_localPoint = other.localPoint();
109     m_innerURLElement = other.URLElement();
110     m_scrollbar = other.scrollbar();
111     m_isFirstLetter = other.m_isFirstLetter;
112     m_isOverWidget = other.isOverWidget();
113 
114     // Only copy the NodeSet in case of rect hit test.
115     m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
116 
117     return *this;
118 }
119 
renderer() const120 RenderObject* HitTestResult::renderer() const
121 {
122     if (!m_innerNode)
123         return 0;
124     RenderObject* renderer = m_innerNode->renderer();
125     if (!m_isFirstLetter || !renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragment())
126         return renderer;
127     return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter();
128 }
129 
setToNodesInDocumentTreeScope()130 void HitTestResult::setToNodesInDocumentTreeScope()
131 {
132     if (Node* node = innerNode()) {
133         node = node->document().ancestorInThisScope(node);
134         setInnerNode(node);
135     }
136 
137     if (Node* node = innerNonSharedNode()) {
138         node = node->document().ancestorInThisScope(node);
139         setInnerNonSharedNode(node);
140     }
141 }
142 
setToShadowHostIfInUserAgentShadowRoot()143 void HitTestResult::setToShadowHostIfInUserAgentShadowRoot()
144 {
145     if (Node* node = innerNode()) {
146         if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
147             if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
148                 setInnerNode(node->shadowHost());
149         }
150     }
151 
152     if (Node* node = innerNonSharedNode()) {
153         if (ShadowRoot* containingShadowRoot = node->containingShadowRoot()) {
154             if (containingShadowRoot->type() == ShadowRoot::UserAgentShadowRoot)
155                 setInnerNonSharedNode(node->shadowHost());
156         }
157     }
158 }
159 
setInnerNode(Node * n)160 void HitTestResult::setInnerNode(Node* n)
161 {
162     m_innerPossiblyPseudoNode = n;
163     if (n && n->isPseudoElement())
164         n = n->parentOrShadowHostNode();
165     m_innerNode = n;
166 }
167 
setInnerNonSharedNode(Node * n)168 void HitTestResult::setInnerNonSharedNode(Node* n)
169 {
170     if (n && n->isPseudoElement())
171         n = n->parentOrShadowHostNode();
172     m_innerNonSharedNode = n;
173 }
174 
setURLElement(Element * n)175 void HitTestResult::setURLElement(Element* n)
176 {
177     m_innerURLElement = n;
178 }
179 
setScrollbar(Scrollbar * s)180 void HitTestResult::setScrollbar(Scrollbar* s)
181 {
182     m_scrollbar = s;
183 }
184 
innerNodeFrame() const185 Frame* HitTestResult::innerNodeFrame() const
186 {
187     if (m_innerNonSharedNode)
188         return m_innerNonSharedNode->document().frame();
189     if (m_innerNode)
190         return m_innerNode->document().frame();
191     return 0;
192 }
193 
targetFrame() const194 Frame* HitTestResult::targetFrame() const
195 {
196     if (!m_innerURLElement)
197         return 0;
198 
199     Frame* frame = m_innerURLElement->document().frame();
200     if (!frame)
201         return 0;
202 
203     return frame->tree().find(m_innerURLElement->target());
204 }
205 
isSelected() const206 bool HitTestResult::isSelected() const
207 {
208     if (!m_innerNonSharedNode)
209         return false;
210 
211     if (Frame* frame = m_innerNonSharedNode->document().frame())
212         return frame->selection().contains(m_hitTestLocation.point());
213     return false;
214 }
215 
spellingToolTip(TextDirection & dir) const216 String HitTestResult::spellingToolTip(TextDirection& dir) const
217 {
218     dir = LTR;
219     // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
220     // currently supply strings, but maybe someday markers associated with misspelled words will also.
221     if (!m_innerNonSharedNode)
222         return String();
223 
224     DocumentMarker* marker = m_innerNonSharedNode->document().markers()->markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Grammar);
225     if (!marker)
226         return String();
227 
228     if (RenderObject* renderer = m_innerNonSharedNode->renderer())
229         dir = renderer->style()->direction();
230     return marker->description();
231 }
232 
title(TextDirection & dir) const233 String HitTestResult::title(TextDirection& dir) const
234 {
235     dir = LTR;
236     // Find the title in the nearest enclosing DOM node.
237     // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
238     for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
239         if (titleNode->isElementNode()) {
240             String title = toElement(titleNode)->title();
241             if (!title.isEmpty()) {
242                 if (RenderObject* renderer = titleNode->renderer())
243                     dir = renderer->style()->direction();
244                 return title;
245             }
246         }
247     }
248     return String();
249 }
250 
altDisplayString() const251 const AtomicString& HitTestResult::altDisplayString() const
252 {
253     if (!m_innerNonSharedNode)
254         return nullAtom;
255 
256     if (m_innerNonSharedNode->hasTagName(imgTag)) {
257         HTMLImageElement* image = toHTMLImageElement(m_innerNonSharedNode);
258         return image->getAttribute(altAttr);
259     }
260 
261     if (m_innerNonSharedNode->hasTagName(inputTag)) {
262         HTMLInputElement* input = toHTMLInputElement(m_innerNonSharedNode);
263         return input->alt();
264     }
265 
266     return nullAtom;
267 }
268 
image() const269 Image* HitTestResult::image() const
270 {
271     if (!m_innerNonSharedNode)
272         return 0;
273 
274     RenderObject* renderer = m_innerNonSharedNode->renderer();
275     if (renderer && renderer->isImage()) {
276         RenderImage* image = toRenderImage(renderer);
277         if (image->cachedImage() && !image->cachedImage()->errorOccurred())
278             return image->cachedImage()->imageForRenderer(image);
279     }
280 
281     return 0;
282 }
283 
imageRect() const284 IntRect HitTestResult::imageRect() const
285 {
286     if (!image())
287         return IntRect();
288     return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
289 }
290 
absoluteImageURL() const291 KURL HitTestResult::absoluteImageURL() const
292 {
293     if (!m_innerNonSharedNode)
294         return KURL();
295 
296     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
297         return KURL();
298 
299     AtomicString urlString;
300     if (m_innerNonSharedNode->hasTagName(embedTag)
301         || m_innerNonSharedNode->hasTagName(imgTag)
302         || m_innerNonSharedNode->hasTagName(inputTag)
303         || m_innerNonSharedNode->hasTagName(objectTag)
304         || m_innerNonSharedNode->hasTagName(SVGNames::imageTag)
305        ) {
306         urlString = toElement(m_innerNonSharedNode)->imageSourceURL();
307     } else
308         return KURL();
309 
310     return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
311 }
312 
absoluteMediaURL() const313 KURL HitTestResult::absoluteMediaURL() const
314 {
315     if (HTMLMediaElement* mediaElt = mediaElement())
316         return mediaElt->currentSrc();
317     return KURL();
318 }
319 
mediaElement() const320 HTMLMediaElement* HitTestResult::mediaElement() const
321 {
322     if (!m_innerNonSharedNode)
323         return 0;
324 
325     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
326         return 0;
327 
328     if (isHTMLVideoElement(m_innerNonSharedNode.get()) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag))
329         return toHTMLMediaElement(m_innerNonSharedNode.get());
330     return 0;
331 }
332 
absoluteLinkURL() const333 KURL HitTestResult::absoluteLinkURL() const
334 {
335     if (!m_innerURLElement)
336         return KURL();
337 
338     AtomicString urlString;
339     if (isHTMLAnchorElement(m_innerURLElement.get()) || isHTMLAreaElement(m_innerURLElement.get()) || m_innerURLElement->hasTagName(linkTag))
340         urlString = m_innerURLElement->getAttribute(hrefAttr);
341     else if (m_innerURLElement->hasTagName(SVGNames::aTag))
342         urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr);
343     else
344         return KURL();
345 
346     return m_innerURLElement->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
347 }
348 
isLiveLink() const349 bool HitTestResult::isLiveLink() const
350 {
351     if (!m_innerURLElement)
352         return false;
353 
354     if (isHTMLAnchorElement(m_innerURLElement.get()))
355         return toHTMLAnchorElement(m_innerURLElement)->isLiveLink();
356 
357     if (m_innerURLElement->hasTagName(SVGNames::aTag))
358         return m_innerURLElement->isLink();
359 
360     return false;
361 }
362 
isMisspelled() const363 bool HitTestResult::isMisspelled() const
364 {
365     if (!targetNode())
366         return false;
367     VisiblePosition pos(targetNode()->renderer()->positionForPoint(localPoint()));
368     if (pos.isNull())
369         return false;
370     return m_innerNonSharedNode->document().markers()->markersInRange(
371         makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers()).size() > 0;
372 }
373 
isOverLink() const374 bool HitTestResult::isOverLink() const
375 {
376     return m_innerURLElement && m_innerURLElement->isLink();
377 }
378 
titleDisplayString() const379 String HitTestResult::titleDisplayString() const
380 {
381     if (!m_innerURLElement)
382         return String();
383 
384     return m_innerURLElement->title();
385 }
386 
textContent() const387 String HitTestResult::textContent() const
388 {
389     if (!m_innerURLElement)
390         return String();
391     return m_innerURLElement->textContent();
392 }
393 
394 // FIXME: This function needs a better name and may belong in a different class. It's not
395 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
396 // function would make more sense in the ContextMenu class, except that WebElementDictionary
397 // hooks into it. Anyway, we should architect this better.
isContentEditable() const398 bool HitTestResult::isContentEditable() const
399 {
400     if (!m_innerNonSharedNode)
401         return false;
402 
403     if (isHTMLTextAreaElement(m_innerNonSharedNode.get()))
404         return true;
405 
406     if (m_innerNonSharedNode->hasTagName(inputTag))
407         return toHTMLInputElement(m_innerNonSharedNode)->isTextField();
408 
409     return m_innerNonSharedNode->rendererIsEditable();
410 }
411 
addNodeToRectBasedTestResult(Node * node,const HitTestRequest & request,const HitTestLocation & locationInContainer,const LayoutRect & rect)412 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
413 {
414     // If it is not a rect-based hit test, this method has to be no-op.
415     // Return false, so the hit test stops.
416     if (!isRectBasedTest())
417         return false;
418 
419     // If node is null, return true so the hit test can continue.
420     if (!node)
421         return true;
422 
423     if (request.disallowsShadowContent())
424         node = node->document().ancestorInThisScope(node);
425 
426     mutableRectBasedTestResult().add(node);
427 
428     bool regionFilled = rect.contains(locationInContainer.boundingBox());
429     return !regionFilled;
430 }
431 
addNodeToRectBasedTestResult(Node * node,const HitTestRequest & request,const HitTestLocation & locationInContainer,const FloatRect & rect)432 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
433 {
434     // If it is not a rect-based hit test, this method has to be no-op.
435     // Return false, so the hit test stops.
436     if (!isRectBasedTest())
437         return false;
438 
439     // If node is null, return true so the hit test can continue.
440     if (!node)
441         return true;
442 
443     if (request.disallowsShadowContent())
444         node = node->document().ancestorInThisScope(node);
445 
446     mutableRectBasedTestResult().add(node);
447 
448     bool regionFilled = rect.contains(locationInContainer.boundingBox());
449     return !regionFilled;
450 }
451 
append(const HitTestResult & other)452 void HitTestResult::append(const HitTestResult& other)
453 {
454     ASSERT(isRectBasedTest() && other.isRectBasedTest());
455 
456     if (!m_scrollbar && other.scrollbar()) {
457         setScrollbar(other.scrollbar());
458     }
459 
460     if (!m_innerNode && other.innerNode()) {
461         m_innerNode = other.innerNode();
462         m_innerPossiblyPseudoNode = other.innerPossiblyPseudoNode();
463         m_innerNonSharedNode = other.innerNonSharedNode();
464         m_localPoint = other.localPoint();
465         m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
466         m_innerURLElement = other.URLElement();
467         m_isOverWidget = other.isOverWidget();
468     }
469 
470     if (other.m_rectBasedTestResult) {
471         NodeSet& set = mutableRectBasedTestResult();
472         for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
473             set.add(it->get());
474     }
475 }
476 
rectBasedTestResult() const477 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
478 {
479     if (!m_rectBasedTestResult)
480         m_rectBasedTestResult = adoptPtr(new NodeSet);
481     return *m_rectBasedTestResult;
482 }
483 
mutableRectBasedTestResult()484 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
485 {
486     if (!m_rectBasedTestResult)
487         m_rectBasedTestResult = adoptPtr(new NodeSet);
488     return *m_rectBasedTestResult;
489 }
490 
targetNode() const491 Node* HitTestResult::targetNode() const
492 {
493     Node* node = innerNode();
494     if (!node)
495         return 0;
496     if (node->inDocument())
497         return node;
498 
499     Element* element = node->parentElement();
500     if (element && element->inDocument())
501         return element;
502 
503     return node;
504 }
505 
innerElement() const506 Element* HitTestResult::innerElement() const
507 {
508     for (Node* node = m_innerNode.get(); node; node = NodeRenderingTraversal::parent(node)) {
509         if (node->isElementNode())
510             return toElement(node);
511     }
512 
513     return 0;
514 }
515 
516 } // namespace WebCore
517