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