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