Factor HitTestPoint out of HitTestResult.
[WebKit-https.git] / Source / WebCore / 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 "HitTestResult.h"
24
25 #include "DocumentMarkerController.h"
26 #include "Frame.h"
27 #include "FrameSelection.h"
28 #include "FrameTree.h"
29 #include "HTMLAnchorElement.h"
30 #include "HTMLVideoElement.h"
31 #include "HTMLImageElement.h"
32 #include "HTMLInputElement.h"
33 #include "HTMLMediaElement.h"
34 #include "HTMLNames.h"
35 #include "HTMLParserIdioms.h"
36 #include "HTMLPlugInImageElement.h"
37 #include "RenderBlock.h"
38 #include "RenderImage.h"
39 #include "RenderInline.h"
40 #include "Scrollbar.h"
41
42 #if ENABLE(SVG)
43 #include "SVGNames.h"
44 #include "XLinkNames.h"
45 #endif
46
47 namespace WebCore {
48
49 using namespace HTMLNames;
50
51 HitTestPoint::HitTestPoint()
52     : m_topPadding(0)
53     , m_rightPadding(0)
54     , m_bottomPadding(0)
55     , m_leftPadding(0)
56     , m_isRectBased(false)
57 {
58 }
59
60 HitTestPoint::HitTestPoint(const LayoutPoint& point)
61     : m_point(point)
62     , m_topPadding(0)
63     , m_rightPadding(0)
64     , m_bottomPadding(0)
65     , m_leftPadding(0)
66     , m_isRectBased(false)
67 {
68 }
69
70 HitTestPoint::HitTestPoint(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
71     : m_point(centerPoint)
72     , m_topPadding(topPadding)
73     , m_rightPadding(rightPadding)
74     , m_bottomPadding(bottomPadding)
75     , m_leftPadding(leftPadding)
76 {
77     // If all padding values passed in are zero then it is not a rect based hit test.
78     m_isRectBased = topPadding || rightPadding || bottomPadding || leftPadding;
79 }
80
81 HitTestPoint::HitTestPoint(const HitTestPoint& other)
82     : m_point(other.m_point)
83     , m_topPadding(other.m_topPadding)
84     , m_rightPadding(other.m_rightPadding)
85     , m_bottomPadding(other.m_bottomPadding)
86     , m_leftPadding(other.m_leftPadding)
87     , m_isRectBased(other.m_isRectBased)
88 {
89 }
90
91 HitTestPoint::~HitTestPoint()
92 {
93 }
94
95 HitTestPoint& HitTestPoint::operator=(const HitTestPoint& other)
96 {
97     m_point = other.m_point;
98     m_topPadding = other.m_topPadding;
99     m_rightPadding = other.m_rightPadding;
100     m_bottomPadding = other.m_bottomPadding;
101     m_leftPadding = other.m_leftPadding;
102     m_isRectBased = other.m_isRectBased;
103
104     return *this;
105 }
106
107 IntRect HitTestPoint::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
108 {
109     IntPoint actualPoint(roundedIntPoint(point));
110     actualPoint -= IntSize(leftPadding, topPadding);
111
112     IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding);
113     // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1".
114     // FIXME: Remove this once non-rect based hit-detection stops using IntRect:intersects.
115     actualPadding += IntSize(1, 1);
116
117     return IntRect(actualPoint, actualPadding);
118 }
119
120 HitTestResult::HitTestResult() : HitTestPoint()
121     , m_isOverWidget(false)
122     , m_shadowContentFilterPolicy(DoNotAllowShadowContent)
123     , m_region(0)
124 {
125 }
126
127 HitTestResult::HitTestResult(const LayoutPoint& point) : HitTestPoint(point)
128     , m_isOverWidget(false)
129     , m_shadowContentFilterPolicy(DoNotAllowShadowContent)
130     , m_region(0)
131 {
132 }
133
134 HitTestResult::HitTestResult(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding, ShadowContentFilterPolicy allowShadowContent)
135     : HitTestPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding)
136     , m_isOverWidget(false)
137     , m_shadowContentFilterPolicy(allowShadowContent)
138     , m_region(0)
139 {
140 }
141
142 HitTestResult::HitTestResult(const HitTestPoint& other, ShadowContentFilterPolicy allowShadowContent)
143     : HitTestPoint(other)
144     , m_isOverWidget(false)
145     , m_shadowContentFilterPolicy(allowShadowContent)
146     , m_region(0)
147 {
148 }
149
150 HitTestResult::HitTestResult(const HitTestResult& other)
151     : HitTestPoint(other)
152     , m_innerNode(other.innerNode())
153     , m_innerNonSharedNode(other.innerNonSharedNode())
154     , m_localPoint(other.localPoint())
155     , m_innerURLElement(other.URLElement())
156     , m_scrollbar(other.scrollbar())
157     , m_isOverWidget(other.isOverWidget())
158     , m_shadowContentFilterPolicy(other.shadowContentFilterPolicy())
159     , m_region(other.region())
160 {
161     // Only copy the NodeSet in case of rect hit test.
162     m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
163 }
164
165 HitTestResult::~HitTestResult()
166 {
167 }
168
169 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
170 {
171     HitTestPoint::operator=(other);
172     m_innerNode = other.innerNode();
173     m_innerNonSharedNode = other.innerNonSharedNode();
174     m_localPoint = other.localPoint();
175     m_innerURLElement = other.URLElement();
176     m_scrollbar = other.scrollbar();
177     m_isOverWidget = other.isOverWidget();
178
179     // Only copy the NodeSet in case of rect hit test.
180     m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
181     m_shadowContentFilterPolicy  = other.shadowContentFilterPolicy();
182
183     m_region = other.m_region;
184
185     return *this;
186 }
187
188 void HitTestResult::setToNonShadowAncestor()
189 {
190     Node* node = innerNode();
191     if (node)
192         node = node->shadowAncestorNode();
193     setInnerNode(node);
194     node = innerNonSharedNode();
195     if (node)
196         node = node->shadowAncestorNode();
197     setInnerNonSharedNode(node);
198 }
199
200 void HitTestResult::setInnerNode(Node* n)
201 {
202     m_innerNode = n;
203 }
204     
205 void HitTestResult::setInnerNonSharedNode(Node* n)
206 {
207     m_innerNonSharedNode = n;
208 }
209
210 void HitTestResult::setURLElement(Element* n) 
211
212     m_innerURLElement = n; 
213 }
214
215 void HitTestResult::setScrollbar(Scrollbar* s)
216 {
217     m_scrollbar = s;
218 }
219
220 Frame* HitTestResult::targetFrame() const
221 {
222     if (!m_innerURLElement)
223         return 0;
224
225     Frame* frame = m_innerURLElement->document()->frame();
226     if (!frame)
227         return 0;
228
229     return frame->tree()->find(m_innerURLElement->target());
230 }
231
232 bool HitTestResult::isSelected() const
233 {
234     if (!m_innerNonSharedNode)
235         return false;
236
237     Frame* frame = m_innerNonSharedNode->document()->frame();
238     if (!frame)
239         return false;
240
241     return frame->selection()->contains(point());
242 }
243
244 String HitTestResult::spellingToolTip(TextDirection& dir) const
245 {
246     dir = LTR;
247     // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
248     // currently supply strings, but maybe someday markers associated with misspelled words will also.
249     if (!m_innerNonSharedNode)
250         return String();
251     
252     DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(point(), DocumentMarker::Grammar);
253     if (!marker)
254         return String();
255
256     if (RenderObject* renderer = m_innerNonSharedNode->renderer())
257         dir = renderer->style()->direction();
258     return marker->description();
259 }
260
261 String HitTestResult::replacedString() const
262 {
263     // Return the replaced string associated with this point, if any. This marker is created when a string is autocorrected, 
264     // and is used for generating a contextual menu item that allows it to easily be changed back if desired.
265     if (!m_innerNonSharedNode)
266         return String();
267     
268     DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(point(), DocumentMarker::Replacement);
269     if (!marker)
270         return String();
271     
272     return marker->description();
273 }    
274     
275 String HitTestResult::title(TextDirection& dir) const
276 {
277     dir = LTR;
278     // Find the title in the nearest enclosing DOM node.
279     // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
280     for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
281         if (titleNode->isElementNode()) {
282             String title = static_cast<Element*>(titleNode)->title();
283             if (!title.isEmpty()) {
284                 if (RenderObject* renderer = titleNode->renderer())
285                     dir = renderer->style()->direction();
286                 return title;
287             }
288         }
289     }
290     return String();
291 }
292
293 String HitTestResult::innerTextIfTruncated(TextDirection& dir) const
294 {
295     for (Node* truncatedNode = m_innerNode.get(); truncatedNode; truncatedNode = truncatedNode->parentNode()) {
296         if (!truncatedNode->isElementNode())
297             continue;
298
299         if (RenderObject* renderer = truncatedNode->renderer()) {
300             if (renderer->isRenderBlock()) {
301                 RenderBlock* block = toRenderBlock(renderer);
302                 if (block->style()->textOverflow()) {
303                     for (RootInlineBox* line = block->firstRootBox(); line; line = line->nextRootBox()) {
304                         if (line->hasEllipsisBox()) {
305                             dir = block->style()->direction();
306                             return toElement(truncatedNode)->innerText();
307                         }
308                     }
309                 }
310                 break;
311             }
312         }
313     }
314
315     dir = LTR;
316     return String();
317 }
318
319 String displayString(const String& string, const Node* node)
320 {
321     if (!node)
322         return string;
323     return node->document()->displayStringModifiedByEncoding(string);
324 }
325
326 String HitTestResult::altDisplayString() const
327 {
328     if (!m_innerNonSharedNode)
329         return String();
330     
331     if (m_innerNonSharedNode->hasTagName(imgTag)) {
332         HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get());
333         return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get());
334     }
335     
336     if (m_innerNonSharedNode->hasTagName(inputTag)) {
337         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get());
338         return displayString(input->alt(), m_innerNonSharedNode.get());
339     }
340
341     return String();
342 }
343
344 Image* HitTestResult::image() const
345 {
346     if (!m_innerNonSharedNode)
347         return 0;
348     
349     RenderObject* renderer = m_innerNonSharedNode->renderer();
350     if (renderer && renderer->isImage()) {
351         RenderImage* image = static_cast<WebCore::RenderImage*>(renderer);
352         if (image->cachedImage() && !image->cachedImage()->errorOccurred())
353             return image->cachedImage()->imageForRenderer(image);
354     }
355
356     return 0;
357 }
358
359 IntRect HitTestResult::imageRect() const
360 {
361     if (!image())
362         return IntRect();
363     return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
364 }
365
366 KURL HitTestResult::absoluteImageURL() const
367 {
368     if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
369         return KURL();
370
371     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
372         return KURL();
373
374     AtomicString urlString;
375     if (m_innerNonSharedNode->hasTagName(embedTag)
376         || m_innerNonSharedNode->hasTagName(imgTag)
377         || m_innerNonSharedNode->hasTagName(inputTag)
378         || m_innerNonSharedNode->hasTagName(objectTag)    
379 #if ENABLE(SVG)
380         || m_innerNonSharedNode->hasTagName(SVGNames::imageTag)
381 #endif
382        ) {
383         Element* element = static_cast<Element*>(m_innerNonSharedNode.get());
384         urlString = element->getAttribute(element->imageSourceAttributeName());
385     } else
386         return KURL();
387
388     return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
389 }
390
391 KURL HitTestResult::absolutePDFURL() const
392 {
393     if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
394         return KURL();
395
396     if (!m_innerNonSharedNode->hasTagName(embedTag) && !m_innerNonSharedNode->hasTagName(objectTag))
397         return KURL();
398
399     HTMLPlugInImageElement* element = static_cast<HTMLPlugInImageElement*>(m_innerNonSharedNode.get());
400     KURL url = m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(element->url()));
401     if (!url.isValid())
402         return KURL();
403
404     if (element->serviceType() == "application/pdf" || (element->serviceType().isEmpty() && url.path().lower().endsWith(".pdf")))
405         return url;
406     return KURL();
407 }
408
409 KURL HitTestResult::absoluteMediaURL() const
410 {
411 #if ENABLE(VIDEO)
412     if (HTMLMediaElement* mediaElt = mediaElement())
413         return mediaElt->currentSrc();
414     return KURL();
415 #else
416     return KURL();
417 #endif
418 }
419
420 bool HitTestResult::mediaSupportsFullscreen() const
421 {
422 #if ENABLE(VIDEO)
423     HTMLMediaElement* mediaElt(mediaElement());
424     return (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag) && mediaElt->supportsFullscreen());
425 #else
426     return false;
427 #endif
428 }
429
430 #if ENABLE(VIDEO)
431 HTMLMediaElement* HitTestResult::mediaElement() const
432 {
433     if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
434         return 0;
435
436     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
437         return 0;
438
439     if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag))
440         return static_cast<HTMLMediaElement*>(m_innerNonSharedNode.get());
441     return 0;
442 }
443 #endif
444
445 void HitTestResult::toggleMediaControlsDisplay() const
446 {
447 #if ENABLE(VIDEO)
448     if (HTMLMediaElement* mediaElt = mediaElement())
449         mediaElt->setControls(!mediaElt->controls());
450 #endif
451 }
452
453 void HitTestResult::toggleMediaLoopPlayback() const
454 {
455 #if ENABLE(VIDEO)
456     if (HTMLMediaElement* mediaElt = mediaElement())
457         mediaElt->setLoop(!mediaElt->loop());
458 #endif
459 }
460
461 void HitTestResult::enterFullscreenForVideo() const
462 {
463 #if ENABLE(VIDEO)
464     HTMLMediaElement* mediaElt(mediaElement());
465     if (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag)) {
466         HTMLVideoElement* videoElt = static_cast<HTMLVideoElement*>(mediaElt);
467         if (!videoElt->isFullscreen() && mediaElt->supportsFullscreen())
468             videoElt->enterFullscreen();
469     }
470 #endif
471 }
472
473 bool HitTestResult::mediaControlsEnabled() const
474 {
475 #if ENABLE(VIDEO)
476     if (HTMLMediaElement* mediaElt = mediaElement())
477         return mediaElt->controls();
478 #endif
479     return false;
480 }
481
482 bool HitTestResult::mediaLoopEnabled() const
483 {
484 #if ENABLE(VIDEO)
485     if (HTMLMediaElement* mediaElt = mediaElement())
486         return mediaElt->loop();
487 #endif
488     return false;
489 }
490
491 bool HitTestResult::mediaPlaying() const
492 {
493 #if ENABLE(VIDEO)
494     if (HTMLMediaElement* mediaElt = mediaElement())
495         return !mediaElt->paused();
496 #endif
497     return false;
498 }
499
500 void HitTestResult::toggleMediaPlayState() const
501 {
502 #if ENABLE(VIDEO)
503     if (HTMLMediaElement* mediaElt = mediaElement())
504         mediaElt->togglePlayState();
505 #endif
506 }
507
508 bool HitTestResult::mediaHasAudio() const
509 {
510 #if ENABLE(VIDEO)
511     if (HTMLMediaElement* mediaElt = mediaElement())
512         return mediaElt->hasAudio();
513 #endif
514     return false;
515 }
516
517 bool HitTestResult::mediaIsVideo() const
518 {
519 #if ENABLE(VIDEO)
520     if (HTMLMediaElement* mediaElt = mediaElement())
521         return mediaElt->hasTagName(HTMLNames::videoTag);
522 #endif
523     return false;
524 }
525
526 bool HitTestResult::mediaMuted() const
527 {
528 #if ENABLE(VIDEO)
529     if (HTMLMediaElement* mediaElt = mediaElement())
530         return mediaElt->muted();
531 #endif
532     return false;
533 }
534
535 void HitTestResult::toggleMediaMuteState() const
536 {
537 #if ENABLE(VIDEO)
538     if (HTMLMediaElement* mediaElt = mediaElement())
539         mediaElt->setMuted(!mediaElt->muted());
540 #endif
541 }
542
543 KURL HitTestResult::absoluteLinkURL() const
544 {
545     if (!(m_innerURLElement && m_innerURLElement->document()))
546         return KURL();
547
548     AtomicString urlString;
549     if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag))
550         urlString = m_innerURLElement->getAttribute(hrefAttr);
551 #if ENABLE(SVG)
552     else if (m_innerURLElement->hasTagName(SVGNames::aTag))
553         urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr);
554 #endif
555     else
556         return KURL();
557
558     return m_innerURLElement->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
559 }
560
561 bool HitTestResult::isLiveLink() const
562 {
563     if (!(m_innerURLElement && m_innerURLElement->document()))
564         return false;
565
566     if (m_innerURLElement->hasTagName(aTag))
567         return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink();
568 #if ENABLE(SVG)
569     if (m_innerURLElement->hasTagName(SVGNames::aTag))
570         return m_innerURLElement->isLink();
571 #endif
572
573     return false;
574 }
575
576 String HitTestResult::titleDisplayString() const
577 {
578     if (!m_innerURLElement)
579         return String();
580     
581     return displayString(m_innerURLElement->title(), m_innerURLElement.get());
582 }
583
584 String HitTestResult::textContent() const
585 {
586     if (!m_innerURLElement)
587         return String();
588     return m_innerURLElement->textContent();
589 }
590
591 // FIXME: This function needs a better name and may belong in a different class. It's not
592 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
593 // function would make more sense in the ContextMenu class, except that WebElementDictionary 
594 // hooks into it. Anyway, we should architect this better. 
595 bool HitTestResult::isContentEditable() const
596 {
597     if (!m_innerNonSharedNode)
598         return false;
599
600     if (m_innerNonSharedNode->hasTagName(textareaTag))
601         return true;
602
603     if (m_innerNonSharedNode->hasTagName(inputTag))
604         return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField();
605
606     return m_innerNonSharedNode->rendererIsEditable();
607 }
608
609 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const LayoutPoint& pointInContainer, const IntRect& rect)
610 {
611     // If it is not a rect-based hit test, this method has to be no-op.
612     // Return false, so the hit test stops.
613     if (!isRectBasedTest())
614         return false;
615
616     // If node is null, return true so the hit test can continue.
617     if (!node)
618         return true;
619
620     if (m_shadowContentFilterPolicy == DoNotAllowShadowContent)
621         node = node->shadowAncestorNode();
622
623     mutableRectBasedTestResult().add(node);
624
625     if (node->renderer()->isInline()) {
626         for (RenderObject* curr = node->renderer()->parent(); curr; curr = curr->parent()) {
627             if (!curr->isRenderInline())
628                 break;
629             
630             // We need to make sure the nodes for culled inlines get included.
631             RenderInline* currInline = toRenderInline(curr);
632             if (currInline->alwaysCreateLineBoxes())
633                 break;
634             
635             if (currInline->visibleToHitTesting() && currInline->node())
636                 mutableRectBasedTestResult().add(currInline->node()->shadowAncestorNode());
637         }
638     }
639     return !rect.contains(rectForPoint(pointInContainer));
640 }
641
642 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const LayoutPoint& pointInContainer, const FloatRect& rect)
643 {
644     // If it is not a rect-based hit test, this method has to be no-op.
645     // Return false, so the hit test stops.
646     if (!isRectBasedTest())
647         return false;
648
649     // If node is null, return true so the hit test can continue.
650     if (!node)
651         return true;
652
653     if (m_shadowContentFilterPolicy == DoNotAllowShadowContent)
654         node = node->shadowAncestorNode();
655
656     mutableRectBasedTestResult().add(node);
657
658     if (node->renderer()->isInline()) {
659         for (RenderObject* curr = node->renderer()->parent(); curr; curr = curr->parent()) {
660             if (!curr->isRenderInline())
661                 break;
662             
663             // We need to make sure the nodes for culled inlines get included.
664             RenderInline* currInline = toRenderInline(curr);
665             if (currInline->alwaysCreateLineBoxes())
666                 break;
667             
668             if (currInline->visibleToHitTesting() && currInline->node())
669                 mutableRectBasedTestResult().add(currInline->node()->shadowAncestorNode());
670         }
671     }
672     return !rect.contains(rectForPoint(pointInContainer));
673 }
674
675 void HitTestResult::append(const HitTestResult& other)
676 {
677     ASSERT(isRectBasedTest() && other.isRectBasedTest());
678
679     if (!m_innerNode && other.innerNode()) {
680         m_innerNode = other.innerNode();
681         m_innerNonSharedNode = other.innerNonSharedNode();
682         m_localPoint = other.localPoint();
683         m_innerURLElement = other.URLElement();
684         m_scrollbar = other.scrollbar();
685         m_isOverWidget = other.isOverWidget();
686     }
687
688     if (other.m_rectBasedTestResult) {
689         NodeSet& set = mutableRectBasedTestResult();
690         for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
691             set.add(it->get());
692     }
693 }
694
695 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
696 {
697     if (!m_rectBasedTestResult)
698         m_rectBasedTestResult = adoptPtr(new NodeSet);
699     return *m_rectBasedTestResult;
700 }
701
702 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
703 {
704     if (!m_rectBasedTestResult)
705         m_rectBasedTestResult = adoptPtr(new NodeSet);
706     return *m_rectBasedTestResult;
707 }
708
709 } // namespace WebCore