Use is<SVG*Element>() instead of isSVG*Element()
[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* n)
133 {
134     if (n && n->isPseudoElement())
135         n = toPseudoElement(n)->hostElement();
136     m_innerNode = n;
137 }
138     
139 void HitTestResult::setInnerNonSharedNode(Node* n)
140 {
141     if (n && n->isPseudoElement())
142         n = toPseudoElement(n)->hostElement();
143     m_innerNonSharedNode = n;
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 (titleNode->isElementNode()) {
227             String title = toElement(titleNode)->title();
228             if (!title.isEmpty()) {
229                 if (auto renderer = titleNode->renderer())
230                     dir = renderer->style().direction();
231                 return title;
232             }
233         }
234     }
235     return String();
236 }
237
238 String HitTestResult::innerTextIfTruncated(TextDirection& dir) const
239 {
240     for (Node* truncatedNode = m_innerNode.get(); truncatedNode; truncatedNode = truncatedNode->parentNode()) {
241         if (!truncatedNode->isElementNode())
242             continue;
243
244         if (auto renderer = toElement(truncatedNode)->renderer()) {
245             if (renderer->isRenderBlockFlow()) {
246                 RenderBlockFlow* block = toRenderBlockFlow(renderer);
247                 if (block->style().textOverflow()) {
248                     for (RootInlineBox* line = block->firstRootBox(); line; line = line->nextRootBox()) {
249                         if (line->hasEllipsisBox()) {
250                             dir = block->style().direction();
251                             return toElement(truncatedNode)->innerText();
252                         }
253                     }
254                 }
255                 break;
256             }
257         }
258     }
259
260     dir = LTR;
261     return String();
262 }
263
264 String displayString(const String& string, const Node* node)
265 {
266     if (!node)
267         return string;
268     return node->document().displayStringModifiedByEncoding(string);
269 }
270
271 String HitTestResult::altDisplayString() const
272 {
273     if (!m_innerNonSharedNode)
274         return String();
275     
276     if (isHTMLImageElement(*m_innerNonSharedNode)) {
277         HTMLImageElement& image = downcast<HTMLImageElement>(*m_innerNonSharedNode);
278         return displayString(image.fastGetAttribute(altAttr), m_innerNonSharedNode.get());
279     }
280     
281     if (isHTMLInputElement(*m_innerNonSharedNode)) {
282         HTMLInputElement& input = downcast<HTMLInputElement>(*m_innerNonSharedNode);
283         return displayString(input.alt(), m_innerNonSharedNode.get());
284     }
285
286     return String();
287 }
288
289 Image* HitTestResult::image() const
290 {
291     if (!m_innerNonSharedNode)
292         return 0;
293     
294     auto renderer = m_innerNonSharedNode->renderer();
295     if (renderer && renderer->isRenderImage()) {
296         RenderImage* image = toRenderImage(renderer);
297         if (image->cachedImage() && !image->cachedImage()->errorOccurred())
298             return image->cachedImage()->imageForRenderer(image);
299     }
300
301     return 0;
302 }
303
304 IntRect HitTestResult::imageRect() const
305 {
306     if (!image())
307         return IntRect();
308     return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
309 }
310
311 URL HitTestResult::absoluteImageURL() const
312 {
313     if (!m_innerNonSharedNode)
314         return URL();
315
316     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
317         return URL();
318
319     AtomicString urlString;
320     if (m_innerNonSharedNode->hasTagName(embedTag)
321         || isHTMLImageElement(m_innerNonSharedNode.get())
322         || isHTMLInputElement(m_innerNonSharedNode.get())
323         || m_innerNonSharedNode->hasTagName(objectTag)
324         || is<SVGImageElement>(m_innerNonSharedNode.get()))
325         {
326         Element* element = toElement(m_innerNonSharedNode.get());
327         urlString = element->imageSourceURL();
328     } else
329         return URL();
330
331     return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
332 }
333
334 URL HitTestResult::absolutePDFURL() const
335 {
336     if (!m_innerNonSharedNode)
337         return URL();
338
339     if (!isHTMLEmbedElement(*m_innerNonSharedNode) && !isHTMLObjectElement(*m_innerNonSharedNode))
340         return URL();
341
342     HTMLPlugInImageElement& element = toHTMLPlugInImageElement(*m_innerNonSharedNode);
343     URL url = m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(element.url()));
344     if (!url.isValid())
345         return URL();
346
347     if (element.serviceType() == "application/pdf" || (element.serviceType().isEmpty() && url.path().endsWith(".pdf", false)))
348         return url;
349     return URL();
350 }
351
352 URL HitTestResult::absoluteMediaURL() const
353 {
354 #if ENABLE(VIDEO)
355     if (HTMLMediaElement* mediaElt = mediaElement())
356         return mediaElt->currentSrc();
357     return URL();
358 #else
359     return URL();
360 #endif
361 }
362
363 bool HitTestResult::mediaSupportsFullscreen() const
364 {
365 #if ENABLE(VIDEO)
366     HTMLMediaElement* mediaElt(mediaElement());
367     return (mediaElt && isHTMLVideoElement(mediaElt) && mediaElt->supportsFullscreen());
368 #else
369     return false;
370 #endif
371 }
372
373 #if ENABLE(VIDEO)
374 HTMLMediaElement* HitTestResult::mediaElement() const
375 {
376     if (!m_innerNonSharedNode)
377         return 0;
378
379     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
380         return 0;
381
382     if (isHTMLVideoElement(m_innerNonSharedNode.get()) || isHTMLAudioElement(m_innerNonSharedNode.get()))
383         return toHTMLMediaElement(m_innerNonSharedNode.get());
384     return 0;
385 }
386 #endif
387
388 void HitTestResult::toggleMediaControlsDisplay() const
389 {
390 #if ENABLE(VIDEO)
391     if (HTMLMediaElement* mediaElt = mediaElement())
392         mediaElt->setControls(!mediaElt->controls());
393 #endif
394 }
395
396 void HitTestResult::toggleMediaLoopPlayback() const
397 {
398 #if ENABLE(VIDEO)
399     if (HTMLMediaElement* mediaElt = mediaElement())
400         mediaElt->setLoop(!mediaElt->loop());
401 #endif
402 }
403
404 bool HitTestResult::mediaIsInFullscreen() const
405 {
406 #if ENABLE(VIDEO)
407     if (HTMLMediaElement* mediaElement = this->mediaElement())
408         return mediaElement->isVideo() && mediaElement->isFullscreen();
409 #endif
410     return false;
411 }
412
413 void HitTestResult::toggleMediaFullscreenState() const
414 {
415 #if ENABLE(VIDEO)
416     if (HTMLMediaElement* mediaElement = this->mediaElement()) {
417         if (mediaElement->isVideo() && mediaElement->supportsFullscreen()) {
418             UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
419             mediaElement->toggleFullscreenState();
420         }
421     }
422 #endif
423 }
424
425 void HitTestResult::enterFullscreenForVideo() const
426 {
427 #if ENABLE(VIDEO)
428     HTMLMediaElement* mediaElement(this->mediaElement());
429     if (mediaElement && isHTMLVideoElement(mediaElement)) {
430         HTMLVideoElement& videoElement = downcast<HTMLVideoElement>(*mediaElement);
431         if (!videoElement.isFullscreen() && mediaElement->supportsFullscreen()) {
432             UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
433             videoElement.enterFullscreen();
434         }
435     }
436 #endif
437 }
438
439 bool HitTestResult::mediaControlsEnabled() const
440 {
441 #if ENABLE(VIDEO)
442     if (HTMLMediaElement* mediaElement = this->mediaElement())
443         return mediaElement->controls();
444 #endif
445     return false;
446 }
447
448 bool HitTestResult::mediaLoopEnabled() const
449 {
450 #if ENABLE(VIDEO)
451     if (HTMLMediaElement* mediaElt = mediaElement())
452         return mediaElt->loop();
453 #endif
454     return false;
455 }
456
457 bool HitTestResult::mediaPlaying() const
458 {
459 #if ENABLE(VIDEO)
460     if (HTMLMediaElement* mediaElt = mediaElement())
461         return !mediaElt->paused();
462 #endif
463     return false;
464 }
465
466 void HitTestResult::toggleMediaPlayState() const
467 {
468 #if ENABLE(VIDEO)
469     if (HTMLMediaElement* mediaElt = mediaElement())
470         mediaElt->togglePlayState();
471 #endif
472 }
473
474 bool HitTestResult::mediaHasAudio() const
475 {
476 #if ENABLE(VIDEO)
477     if (HTMLMediaElement* mediaElt = mediaElement())
478         return mediaElt->hasAudio();
479 #endif
480     return false;
481 }
482
483 bool HitTestResult::mediaIsVideo() const
484 {
485 #if ENABLE(VIDEO)
486     if (HTMLMediaElement* mediaElt = mediaElement())
487         return isHTMLVideoElement(mediaElt);
488 #endif
489     return false;
490 }
491
492 bool HitTestResult::mediaMuted() const
493 {
494 #if ENABLE(VIDEO)
495     if (HTMLMediaElement* mediaElt = mediaElement())
496         return mediaElt->muted();
497 #endif
498     return false;
499 }
500
501 void HitTestResult::toggleMediaMuteState() const
502 {
503 #if ENABLE(VIDEO)
504     if (HTMLMediaElement* mediaElt = mediaElement())
505         mediaElt->setMuted(!mediaElt->muted());
506 #endif
507 }
508
509 URL HitTestResult::absoluteLinkURL() const
510 {
511     if (m_innerURLElement)
512         return m_innerURLElement->absoluteLinkURL();
513     return URL();
514 }
515
516 bool HitTestResult::isLiveLink() const
517 {
518     if (!m_innerURLElement)
519         return false;
520
521     if (isHTMLAnchorElement(*m_innerURLElement))
522         return downcast<HTMLAnchorElement>(*m_innerURLElement).isLiveLink();
523
524     if (is<SVGAElement>(*m_innerURLElement))
525         return m_innerURLElement->isLink();
526
527     return false;
528 }
529
530 bool HitTestResult::isOverLink() const
531 {
532     return m_innerURLElement && m_innerURLElement->isLink();
533 }
534
535 String HitTestResult::titleDisplayString() const
536 {
537     if (!m_innerURLElement)
538         return String();
539     
540     return displayString(m_innerURLElement->title(), m_innerURLElement.get());
541 }
542
543 String HitTestResult::textContent() const
544 {
545     if (!m_innerURLElement)
546         return String();
547     return m_innerURLElement->textContent();
548 }
549
550 // FIXME: This function needs a better name and may belong in a different class. It's not
551 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
552 // function would make more sense in the ContextMenu class, except that WebElementDictionary 
553 // hooks into it. Anyway, we should architect this better. 
554 bool HitTestResult::isContentEditable() const
555 {
556     if (!m_innerNonSharedNode)
557         return false;
558
559     if (isHTMLTextAreaElement(*m_innerNonSharedNode))
560         return true;
561
562     if (isHTMLInputElement(*m_innerNonSharedNode))
563         return downcast<HTMLInputElement>(*m_innerNonSharedNode).isTextField();
564
565     return m_innerNonSharedNode->hasEditableStyle();
566 }
567
568 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
569 {
570     // If it is not a rect-based hit test, this method has to be no-op.
571     // Return false, so the hit test stops.
572     if (!isRectBasedTest())
573         return false;
574
575     // If node is null, return true so the hit test can continue.
576     if (!node)
577         return true;
578
579     if (request.disallowsShadowContent())
580         node = node->document().ancestorInThisScope(node);
581
582     mutableRectBasedTestResult().add(node);
583
584     bool regionFilled = rect.contains(locationInContainer.boundingBox());
585     return !regionFilled;
586 }
587
588 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
589 {
590     // If it is not a rect-based hit test, this method has to be no-op.
591     // Return false, so the hit test stops.
592     if (!isRectBasedTest())
593         return false;
594
595     // If node is null, return true so the hit test can continue.
596     if (!node)
597         return true;
598
599     if (request.disallowsShadowContent())
600         node = node->document().ancestorInThisScope(node);
601
602     mutableRectBasedTestResult().add(node);
603
604     bool regionFilled = rect.contains(locationInContainer.boundingBox());
605     return !regionFilled;
606 }
607
608 void HitTestResult::append(const HitTestResult& other)
609 {
610     ASSERT(isRectBasedTest() && other.isRectBasedTest());
611
612     if (!m_innerNode && other.innerNode()) {
613         m_innerNode = other.innerNode();
614         m_innerNonSharedNode = other.innerNonSharedNode();
615         m_localPoint = other.localPoint();
616         m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
617         m_innerURLElement = other.URLElement();
618         m_scrollbar = other.scrollbar();
619         m_isOverWidget = other.isOverWidget();
620     }
621
622     if (other.m_rectBasedTestResult) {
623         NodeSet& set = mutableRectBasedTestResult();
624         for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
625             set.add(it->get());
626     }
627 }
628
629 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
630 {
631     if (!m_rectBasedTestResult)
632         m_rectBasedTestResult = std::make_unique<NodeSet>();
633     return *m_rectBasedTestResult;
634 }
635
636 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
637 {
638     if (!m_rectBasedTestResult)
639         m_rectBasedTestResult = std::make_unique<NodeSet>();
640     return *m_rectBasedTestResult;
641 }
642
643 Vector<String> HitTestResult::dictationAlternatives() const
644 {
645     // Return the dictation context handle if the text at this point has DictationAlternative marker, which means this text is
646     if (!m_innerNonSharedNode)
647         return Vector<String>();
648
649     DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(pointInInnerNodeFrame(), DocumentMarker::DictationAlternatives);
650     if (!marker)
651         return Vector<String>();
652
653     Frame* frame = innerNonSharedNode()->document().frame();
654     if (!frame)
655         return Vector<String>();
656
657     return frame->editor().dictationAlternativesForMarker(marker);
658 }
659
660 Node* HitTestResult::targetNode() const
661 {
662     Node* node = innerNode();
663     if (!node)
664         return 0;
665     if (node->inDocument())
666         return node;
667
668     Element* element = node->parentElement();
669     if (element && element->inDocument())
670         return element;
671
672     return node;
673 }
674
675 Element* HitTestResult::innerElement() const
676 {
677     Node* node = m_innerNode.get();
678     if (!node)
679         return 0;
680     if (node->isElementNode())
681         return toElement(node);
682     return node->parentElement();
683 }
684
685 Element* HitTestResult::innerNonSharedElement() const
686 {
687     Node* node = m_innerNonSharedNode.get();
688     if (!node)
689         return 0;
690     if (node->isElementNode())
691         return toElement(node);
692     return node->parentElement();
693 }
694
695 } // namespace WebCore