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