Use is<HTML*Element>() instead of isHTML*Element() - Part 2
[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 (is<HTMLImageElement>(*m_innerNonSharedNode)) {
277         HTMLImageElement& image = downcast<HTMLImageElement>(*m_innerNonSharedNode);
278         return displayString(image.fastGetAttribute(altAttr), m_innerNonSharedNode.get());
279     }
280     
281     if (is<HTMLInputElement>(*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 (is<HTMLEmbedElement>(*m_innerNonSharedNode)
321         || is<HTMLImageElement>(*m_innerNonSharedNode)
322         || is<HTMLInputElement>(*m_innerNonSharedNode)
323         || is<HTMLObjectElement>(*m_innerNonSharedNode)
324         || is<SVGImageElement>(*m_innerNonSharedNode)) {
325         urlString = toElement(*m_innerNonSharedNode).imageSourceURL();
326     } else
327         return URL();
328
329     return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
330 }
331
332 URL HitTestResult::absolutePDFURL() const
333 {
334     if (!m_innerNonSharedNode)
335         return URL();
336
337     if (!is<HTMLEmbedElement>(*m_innerNonSharedNode) && !is<HTMLObjectElement>(*m_innerNonSharedNode))
338         return URL();
339
340     HTMLPlugInImageElement& element = toHTMLPlugInImageElement(*m_innerNonSharedNode);
341     URL url = m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(element.url()));
342     if (!url.isValid())
343         return URL();
344
345     if (element.serviceType() == "application/pdf" || (element.serviceType().isEmpty() && url.path().endsWith(".pdf", false)))
346         return url;
347     return URL();
348 }
349
350 URL HitTestResult::absoluteMediaURL() const
351 {
352 #if ENABLE(VIDEO)
353     if (HTMLMediaElement* mediaElt = mediaElement())
354         return mediaElt->currentSrc();
355     return URL();
356 #else
357     return URL();
358 #endif
359 }
360
361 bool HitTestResult::mediaSupportsFullscreen() const
362 {
363 #if ENABLE(VIDEO)
364     HTMLMediaElement* mediaElt(mediaElement());
365     return (mediaElt && is<HTMLVideoElement>(mediaElt) && mediaElt->supportsFullscreen());
366 #else
367     return false;
368 #endif
369 }
370
371 #if ENABLE(VIDEO)
372 HTMLMediaElement* HitTestResult::mediaElement() const
373 {
374     if (!m_innerNonSharedNode)
375         return 0;
376
377     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
378         return 0;
379
380     if (is<HTMLVideoElement>(m_innerNonSharedNode.get()) || is<HTMLAudioElement>(m_innerNonSharedNode.get()))
381         return toHTMLMediaElement(m_innerNonSharedNode.get());
382     return 0;
383 }
384 #endif
385
386 void HitTestResult::toggleMediaControlsDisplay() const
387 {
388 #if ENABLE(VIDEO)
389     if (HTMLMediaElement* mediaElt = mediaElement())
390         mediaElt->setControls(!mediaElt->controls());
391 #endif
392 }
393
394 void HitTestResult::toggleMediaLoopPlayback() const
395 {
396 #if ENABLE(VIDEO)
397     if (HTMLMediaElement* mediaElt = mediaElement())
398         mediaElt->setLoop(!mediaElt->loop());
399 #endif
400 }
401
402 bool HitTestResult::mediaIsInFullscreen() const
403 {
404 #if ENABLE(VIDEO)
405     if (HTMLMediaElement* mediaElement = this->mediaElement())
406         return mediaElement->isVideo() && mediaElement->isFullscreen();
407 #endif
408     return false;
409 }
410
411 void HitTestResult::toggleMediaFullscreenState() const
412 {
413 #if ENABLE(VIDEO)
414     if (HTMLMediaElement* mediaElement = this->mediaElement()) {
415         if (mediaElement->isVideo() && mediaElement->supportsFullscreen()) {
416             UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
417             mediaElement->toggleFullscreenState();
418         }
419     }
420 #endif
421 }
422
423 void HitTestResult::enterFullscreenForVideo() const
424 {
425 #if ENABLE(VIDEO)
426     HTMLMediaElement* mediaElement(this->mediaElement());
427     if (mediaElement && is<HTMLVideoElement>(mediaElement)) {
428         HTMLVideoElement& videoElement = downcast<HTMLVideoElement>(*mediaElement);
429         if (!videoElement.isFullscreen() && mediaElement->supportsFullscreen()) {
430             UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
431             videoElement.enterFullscreen();
432         }
433     }
434 #endif
435 }
436
437 bool HitTestResult::mediaControlsEnabled() const
438 {
439 #if ENABLE(VIDEO)
440     if (HTMLMediaElement* mediaElement = this->mediaElement())
441         return mediaElement->controls();
442 #endif
443     return false;
444 }
445
446 bool HitTestResult::mediaLoopEnabled() const
447 {
448 #if ENABLE(VIDEO)
449     if (HTMLMediaElement* mediaElt = mediaElement())
450         return mediaElt->loop();
451 #endif
452     return false;
453 }
454
455 bool HitTestResult::mediaPlaying() const
456 {
457 #if ENABLE(VIDEO)
458     if (HTMLMediaElement* mediaElt = mediaElement())
459         return !mediaElt->paused();
460 #endif
461     return false;
462 }
463
464 void HitTestResult::toggleMediaPlayState() const
465 {
466 #if ENABLE(VIDEO)
467     if (HTMLMediaElement* mediaElt = mediaElement())
468         mediaElt->togglePlayState();
469 #endif
470 }
471
472 bool HitTestResult::mediaHasAudio() const
473 {
474 #if ENABLE(VIDEO)
475     if (HTMLMediaElement* mediaElt = mediaElement())
476         return mediaElt->hasAudio();
477 #endif
478     return false;
479 }
480
481 bool HitTestResult::mediaIsVideo() const
482 {
483 #if ENABLE(VIDEO)
484     if (HTMLMediaElement* mediaElt = mediaElement())
485         return is<HTMLVideoElement>(mediaElt);
486 #endif
487     return false;
488 }
489
490 bool HitTestResult::mediaMuted() const
491 {
492 #if ENABLE(VIDEO)
493     if (HTMLMediaElement* mediaElt = mediaElement())
494         return mediaElt->muted();
495 #endif
496     return false;
497 }
498
499 void HitTestResult::toggleMediaMuteState() const
500 {
501 #if ENABLE(VIDEO)
502     if (HTMLMediaElement* mediaElt = mediaElement())
503         mediaElt->setMuted(!mediaElt->muted());
504 #endif
505 }
506
507 URL HitTestResult::absoluteLinkURL() const
508 {
509     if (m_innerURLElement)
510         return m_innerURLElement->absoluteLinkURL();
511     return URL();
512 }
513
514 bool HitTestResult::isLiveLink() const
515 {
516     if (!m_innerURLElement)
517         return false;
518
519     if (is<HTMLAnchorElement>(*m_innerURLElement))
520         return downcast<HTMLAnchorElement>(*m_innerURLElement).isLiveLink();
521
522     if (is<SVGAElement>(*m_innerURLElement))
523         return m_innerURLElement->isLink();
524
525     return false;
526 }
527
528 bool HitTestResult::isOverLink() const
529 {
530     return m_innerURLElement && m_innerURLElement->isLink();
531 }
532
533 String HitTestResult::titleDisplayString() const
534 {
535     if (!m_innerURLElement)
536         return String();
537     
538     return displayString(m_innerURLElement->title(), m_innerURLElement.get());
539 }
540
541 String HitTestResult::textContent() const
542 {
543     if (!m_innerURLElement)
544         return String();
545     return m_innerURLElement->textContent();
546 }
547
548 // FIXME: This function needs a better name and may belong in a different class. It's not
549 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
550 // function would make more sense in the ContextMenu class, except that WebElementDictionary 
551 // hooks into it. Anyway, we should architect this better. 
552 bool HitTestResult::isContentEditable() const
553 {
554     if (!m_innerNonSharedNode)
555         return false;
556
557     if (is<HTMLTextAreaElement>(*m_innerNonSharedNode))
558         return true;
559
560     if (is<HTMLInputElement>(*m_innerNonSharedNode))
561         return downcast<HTMLInputElement>(*m_innerNonSharedNode).isTextField();
562
563     return m_innerNonSharedNode->hasEditableStyle();
564 }
565
566 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
567 {
568     // If it is not a rect-based hit test, this method has to be no-op.
569     // Return false, so the hit test stops.
570     if (!isRectBasedTest())
571         return false;
572
573     // If node is null, return true so the hit test can continue.
574     if (!node)
575         return true;
576
577     if (request.disallowsShadowContent())
578         node = node->document().ancestorInThisScope(node);
579
580     mutableRectBasedTestResult().add(node);
581
582     bool regionFilled = rect.contains(locationInContainer.boundingBox());
583     return !regionFilled;
584 }
585
586 bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
587 {
588     // If it is not a rect-based hit test, this method has to be no-op.
589     // Return false, so the hit test stops.
590     if (!isRectBasedTest())
591         return false;
592
593     // If node is null, return true so the hit test can continue.
594     if (!node)
595         return true;
596
597     if (request.disallowsShadowContent())
598         node = node->document().ancestorInThisScope(node);
599
600     mutableRectBasedTestResult().add(node);
601
602     bool regionFilled = rect.contains(locationInContainer.boundingBox());
603     return !regionFilled;
604 }
605
606 void HitTestResult::append(const HitTestResult& other)
607 {
608     ASSERT(isRectBasedTest() && other.isRectBasedTest());
609
610     if (!m_innerNode && other.innerNode()) {
611         m_innerNode = other.innerNode();
612         m_innerNonSharedNode = other.innerNonSharedNode();
613         m_localPoint = other.localPoint();
614         m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
615         m_innerURLElement = other.URLElement();
616         m_scrollbar = other.scrollbar();
617         m_isOverWidget = other.isOverWidget();
618     }
619
620     if (other.m_rectBasedTestResult) {
621         NodeSet& set = mutableRectBasedTestResult();
622         for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
623             set.add(it->get());
624     }
625 }
626
627 const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
628 {
629     if (!m_rectBasedTestResult)
630         m_rectBasedTestResult = std::make_unique<NodeSet>();
631     return *m_rectBasedTestResult;
632 }
633
634 HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
635 {
636     if (!m_rectBasedTestResult)
637         m_rectBasedTestResult = std::make_unique<NodeSet>();
638     return *m_rectBasedTestResult;
639 }
640
641 Vector<String> HitTestResult::dictationAlternatives() const
642 {
643     // Return the dictation context handle if the text at this point has DictationAlternative marker, which means this text is
644     if (!m_innerNonSharedNode)
645         return Vector<String>();
646
647     DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(pointInInnerNodeFrame(), DocumentMarker::DictationAlternatives);
648     if (!marker)
649         return Vector<String>();
650
651     Frame* frame = innerNonSharedNode()->document().frame();
652     if (!frame)
653         return Vector<String>();
654
655     return frame->editor().dictationAlternativesForMarker(marker);
656 }
657
658 Node* HitTestResult::targetNode() const
659 {
660     Node* node = innerNode();
661     if (!node)
662         return 0;
663     if (node->inDocument())
664         return node;
665
666     Element* element = node->parentElement();
667     if (element && element->inDocument())
668         return element;
669
670     return node;
671 }
672
673 Element* HitTestResult::innerElement() const
674 {
675     Node* node = m_innerNode.get();
676     if (!node)
677         return 0;
678     if (node->isElementNode())
679         return toElement(node);
680     return node->parentElement();
681 }
682
683 Element* HitTestResult::innerNonSharedElement() const
684 {
685     Node* node = m_innerNonSharedNode.get();
686     if (!node)
687         return 0;
688     if (node->isElementNode())
689         return toElement(node);
690     return node->parentElement();
691 }
692
693 } // namespace WebCore