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