Use "= default" to denote default constructor or destructor
[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 = 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 = 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()) {
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 = 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 #if ENABLE(ATTACHMENT_ELEMENT)
345 URL HitTestResult::absoluteAttachmentURL() const
346 {
347     if (!m_innerNonSharedNode)
348         return URL();
349     
350     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isAttachment()))
351         return URL();
352     
353     if (!is<HTMLAttachmentElement>(*m_innerNonSharedNode))
354         return URL();
355     File* attachmentFile = downcast<HTMLAttachmentElement>(*m_innerNonSharedNode).file();
356     if (!attachmentFile)
357         return URL();
358     
359     return URL::fileURLWithFileSystemPath(attachmentFile->path());
360 }
361 #endif
362
363 URL HitTestResult::absoluteImageURL() const
364 {
365     if (!m_innerNonSharedNode)
366         return URL();
367
368     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
369         return URL();
370
371     AtomicString urlString;
372     if (is<HTMLEmbedElement>(*m_innerNonSharedNode)
373         || is<HTMLImageElement>(*m_innerNonSharedNode)
374         || is<HTMLInputElement>(*m_innerNonSharedNode)
375         || is<HTMLObjectElement>(*m_innerNonSharedNode)
376         || is<SVGImageElement>(*m_innerNonSharedNode)) {
377         urlString = downcast<Element>(*m_innerNonSharedNode).imageSourceURL();
378     } else
379         return URL();
380
381     return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
382 }
383
384 URL HitTestResult::absolutePDFURL() const
385 {
386     if (!m_innerNonSharedNode)
387         return URL();
388
389     if (!is<HTMLEmbedElement>(*m_innerNonSharedNode) && !is<HTMLObjectElement>(*m_innerNonSharedNode))
390         return URL();
391
392     HTMLPlugInImageElement& element = downcast<HTMLPlugInImageElement>(*m_innerNonSharedNode);
393     URL url = m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(element.url()));
394     if (!url.isValid())
395         return URL();
396
397     if (element.serviceType() == "application/pdf" || (element.serviceType().isEmpty() && url.path().endsWith(".pdf", false)))
398         return url;
399     return URL();
400 }
401
402 URL HitTestResult::absoluteMediaURL() const
403 {
404 #if ENABLE(VIDEO)
405     if (HTMLMediaElement* mediaElt = mediaElement())
406         return mediaElt->currentSrc();
407     return URL();
408 #else
409     return URL();
410 #endif
411 }
412
413 bool HitTestResult::mediaSupportsFullscreen() const
414 {
415 #if ENABLE(VIDEO)
416     HTMLMediaElement* mediaElt(mediaElement());
417     return is<HTMLVideoElement>(mediaElt) && mediaElt->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard);
418 #else
419     return false;
420 #endif
421 }
422
423 #if ENABLE(VIDEO)
424 HTMLMediaElement* HitTestResult::mediaElement() const
425 {
426     if (!m_innerNonSharedNode)
427         return nullptr;
428
429     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
430         return nullptr;
431
432     if (is<HTMLMediaElement>(*m_innerNonSharedNode))
433         return downcast<HTMLMediaElement>(m_innerNonSharedNode.get());
434     return nullptr;
435 }
436 #endif
437
438 void HitTestResult::toggleMediaControlsDisplay() const
439 {
440 #if ENABLE(VIDEO)
441     if (HTMLMediaElement* mediaElt = mediaElement())
442         mediaElt->setControls(!mediaElt->controls());
443 #endif
444 }
445
446 void HitTestResult::toggleMediaLoopPlayback() const
447 {
448 #if ENABLE(VIDEO)
449     if (HTMLMediaElement* mediaElt = mediaElement())
450         mediaElt->setLoop(!mediaElt->loop());
451 #endif
452 }
453
454 bool HitTestResult::mediaIsInFullscreen() const
455 {
456 #if ENABLE(VIDEO)
457     if (HTMLMediaElement* mediaElement = this->mediaElement())
458         return mediaElement->isVideo() && mediaElement->isStandardFullscreen();
459 #endif
460     return false;
461 }
462
463 void HitTestResult::toggleMediaFullscreenState() const
464 {
465 #if ENABLE(VIDEO)
466     if (HTMLMediaElement* mediaElement = this->mediaElement()) {
467         if (mediaElement->isVideo() && mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
468             UserGestureIndicator indicator(ProcessingUserGesture, &mediaElement->document());
469             mediaElement->toggleStandardFullscreenState();
470         }
471     }
472 #endif
473 }
474
475 void HitTestResult::enterFullscreenForVideo() const
476 {
477 #if ENABLE(VIDEO)
478     HTMLMediaElement* mediaElement(this->mediaElement());
479     if (is<HTMLVideoElement>(mediaElement)) {
480         HTMLVideoElement& videoElement = downcast<HTMLVideoElement>(*mediaElement);
481         if (!videoElement.isFullscreen() && mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
482             UserGestureIndicator indicator(ProcessingUserGesture, &mediaElement->document());
483             videoElement.enterFullscreen();
484         }
485     }
486 #endif
487 }
488
489 bool HitTestResult::mediaControlsEnabled() const
490 {
491 #if ENABLE(VIDEO)
492     if (HTMLMediaElement* mediaElement = this->mediaElement())
493         return mediaElement->controls();
494 #endif
495     return false;
496 }
497
498 bool HitTestResult::mediaLoopEnabled() const
499 {
500 #if ENABLE(VIDEO)
501     if (HTMLMediaElement* mediaElt = mediaElement())
502         return mediaElt->loop();
503 #endif
504     return false;
505 }
506
507 bool HitTestResult::mediaPlaying() const
508 {
509 #if ENABLE(VIDEO)
510     if (HTMLMediaElement* mediaElt = mediaElement())
511         return !mediaElt->paused();
512 #endif
513     return false;
514 }
515
516 void HitTestResult::toggleMediaPlayState() const
517 {
518 #if ENABLE(VIDEO)
519     if (HTMLMediaElement* mediaElt = mediaElement())
520         mediaElt->togglePlayState();
521 #endif
522 }
523
524 bool HitTestResult::mediaHasAudio() const
525 {
526 #if ENABLE(VIDEO)
527     if (HTMLMediaElement* mediaElt = mediaElement())
528         return mediaElt->hasAudio();
529 #endif
530     return false;
531 }
532
533 bool HitTestResult::mediaIsVideo() const
534 {
535 #if ENABLE(VIDEO)
536     if (HTMLMediaElement* mediaElt = mediaElement())
537         return is<HTMLVideoElement>(*mediaElt);
538 #endif
539     return false;
540 }
541
542 bool HitTestResult::mediaMuted() const
543 {
544 #if ENABLE(VIDEO)
545     if (HTMLMediaElement* mediaElt = mediaElement())
546         return mediaElt->muted();
547 #endif
548     return false;
549 }
550
551 void HitTestResult::toggleMediaMuteState() const
552 {
553 #if ENABLE(VIDEO)
554     if (HTMLMediaElement* mediaElt = mediaElement())
555         mediaElt->setMuted(!mediaElt->muted());
556 #endif
557 }
558
559 bool HitTestResult::isDownloadableMedia() const
560 {
561 #if ENABLE(VIDEO)
562     if (HTMLMediaElement* mediaElt = mediaElement())
563         return mediaElt->canSaveMediaData();
564 #endif
565
566     return false;
567 }
568
569 bool HitTestResult::isOverTextInsideFormControlElement() const
570 {
571     Node* node = innerNode();
572     if (!node)
573         return false;
574
575     if (!is<HTMLTextFormControlElement>(*node))
576         return false;
577
578     Frame* frame = node->document().frame();
579     if (!frame)
580         return false;
581
582     IntPoint framePoint = roundedPointInInnerNodeFrame();
583     if (!frame->rangeForPoint(framePoint))
584         return false;
585
586     VisiblePosition position = frame->visiblePositionForPoint(framePoint);
587     if (position.isNull())
588         return false;
589
590     RefPtr<Range> wordRange = enclosingTextUnitOfGranularity(position, WordGranularity, DirectionForward);
591     if (!wordRange)
592         return false;
593
594     return !wordRange->text().isEmpty();
595 }
596
597 bool HitTestResult::allowsCopy() const
598 {
599     Node* node = innerNode();
600     if (!node)
601         return false;
602
603     RenderObject* renderer = node->renderer();
604     if (!renderer)
605         return false;
606
607     bool isUserSelectNone = renderer->style().userSelect() == SELECT_NONE;
608     bool isPasswordField = is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).isPasswordField();
609     return !isPasswordField && !isUserSelectNone;
610 }
611
612 URL HitTestResult::absoluteLinkURL() const
613 {
614     if (m_innerURLElement)
615         return m_innerURLElement->absoluteLinkURL();
616     return URL();
617 }
618
619 bool HitTestResult::isOverLink() const
620 {
621     return m_innerURLElement && m_innerURLElement->isLink();
622 }
623
624 String HitTestResult::titleDisplayString() const
625 {
626     if (!m_innerURLElement)
627         return String();
628     
629     return displayString(m_innerURLElement->title(), m_innerURLElement.get());
630 }
631
632 String HitTestResult::textContent() const
633 {
634     if (!m_innerURLElement)
635         return String();
636     return m_innerURLElement->textContent();
637 }
638
639 // FIXME: This function needs a better name and may belong in a different class. It's not
640 // really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this
641 // function would make more sense in the ContextMenu class, except that WebElementDictionary 
642 // hooks into it. Anyway, we should architect this better. 
643 bool HitTestResult::isContentEditable() const
644 {
645     if (!m_innerNonSharedNode)
646         return false;
647
648     if (is<HTMLTextAreaElement>(*m_innerNonSharedNode))
649         return true;
650
651     if (is<HTMLInputElement>(*m_innerNonSharedNode))
652         return downcast<HTMLInputElement>(*m_innerNonSharedNode).isTextField();
653
654     return m_innerNonSharedNode->hasEditableStyle();
655 }
656
657 HitTestProgress HitTestResult::addNodeToListBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
658 {
659     // If it is not a list-based hit test, this method has to be no-op.
660     if (!request.resultIsElementList()) {
661         ASSERT(!isRectBasedTest());
662         return HitTestProgress::Stop;
663     }
664
665     if (!node)
666         return HitTestProgress::Continue;
667
668     if (request.disallowsUserAgentShadowContent() && node->isInUserAgentShadowTree())
669         node = node->document().ancestorNodeInThisScope(node);
670
671     mutableListBasedTestResult().add(node);
672
673     if (request.includesAllElementsUnderPoint())
674         return HitTestProgress::Continue;
675
676     bool regionFilled = rect.contains(locationInContainer.boundingBox());
677     return regionFilled ? HitTestProgress::Stop : HitTestProgress::Continue;
678 }
679
680 HitTestProgress HitTestResult::addNodeToListBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
681 {
682     // If it is not a list-based hit test, this method has to be no-op.
683     if (!request.resultIsElementList()) {
684         ASSERT(!isRectBasedTest());
685         return HitTestProgress::Stop;
686     }
687
688     if (!node)
689         return HitTestProgress::Continue;
690
691     if (request.disallowsUserAgentShadowContent() && node->isInUserAgentShadowTree())
692         node = node->document().ancestorNodeInThisScope(node);
693
694     mutableListBasedTestResult().add(node);
695
696     if (request.includesAllElementsUnderPoint())
697         return HitTestProgress::Continue;
698
699     bool regionFilled = rect.contains(locationInContainer.boundingBox());
700     return regionFilled ? HitTestProgress::Stop : HitTestProgress::Continue;
701 }
702
703 void HitTestResult::append(const HitTestResult& other, const HitTestRequest& request)
704 {
705     ASSERT_UNUSED(request, request.resultIsElementList());
706
707     if (!m_innerNode && other.innerNode()) {
708         m_innerNode = other.innerNode();
709         m_innerNonSharedNode = other.innerNonSharedNode();
710         m_localPoint = other.localPoint();
711         m_pointInInnerNodeFrame = other.m_pointInInnerNodeFrame;
712         m_innerURLElement = other.URLElement();
713         m_scrollbar = other.scrollbar();
714         m_isOverWidget = other.isOverWidget();
715     }
716
717     if (other.m_listBasedTestResult) {
718         NodeSet& set = mutableListBasedTestResult();
719         for (auto node : *other.m_listBasedTestResult)
720             set.add(node.get());
721     }
722 }
723
724 const HitTestResult::NodeSet& HitTestResult::listBasedTestResult() const
725 {
726     if (!m_listBasedTestResult)
727         m_listBasedTestResult = std::make_unique<NodeSet>();
728     return *m_listBasedTestResult;
729 }
730
731 HitTestResult::NodeSet& HitTestResult::mutableListBasedTestResult()
732 {
733     if (!m_listBasedTestResult)
734         m_listBasedTestResult = std::make_unique<NodeSet>();
735     return *m_listBasedTestResult;
736 }
737
738 Vector<String> HitTestResult::dictationAlternatives() const
739 {
740     // Return the dictation context handle if the text at this point has DictationAlternative marker, which means this text is
741     if (!m_innerNonSharedNode)
742         return Vector<String>();
743
744     DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(pointInInnerNodeFrame(), DocumentMarker::DictationAlternatives);
745     if (!marker)
746         return Vector<String>();
747
748     Frame* frame = innerNonSharedNode()->document().frame();
749     if (!frame)
750         return Vector<String>();
751
752     return frame->editor().dictationAlternativesForMarker(*marker);
753 }
754
755 Node* HitTestResult::targetNode() const
756 {
757     Node* node = innerNode();
758     if (!node)
759         return 0;
760     if (node->isConnected())
761         return node;
762
763     Element* element = node->parentElement();
764     if (element && element->isConnected())
765         return element;
766
767     return node;
768 }
769
770 Element* HitTestResult::targetElement() const
771 {
772     for (Node* node = m_innerNode.get(); node; node = node->parentInComposedTree()) {
773         if (is<Element>(*node))
774             return downcast<Element>(node);
775     }
776     return nullptr;
777 }
778
779 Element* HitTestResult::innerNonSharedElement() const
780 {
781     Node* node = m_innerNonSharedNode.get();
782     if (!node)
783         return nullptr;
784     if (is<Element>(*node))
785         return downcast<Element>(node);
786     return node->parentElement();
787 }
788
789 String HitTestResult::linkSuggestedFilename() const
790 {
791     auto* urlElement = URLElement();
792     if (!is<HTMLAnchorElement>(urlElement))
793         return nullAtom();
794     return ResourceResponse::sanitizeSuggestedFilename(urlElement->attributeWithoutSynchronization(HTMLNames::downloadAttr));
795 }
796
797 bool HitTestResult::mediaSupportsEnhancedFullscreen() const
798 {
799 #if PLATFORM(MAC) && ENABLE(VIDEO) && ENABLE(VIDEO_PRESENTATION_MODE)
800     HTMLMediaElement* mediaElt(mediaElement());
801     return is<HTMLVideoElement>(mediaElt) && mediaElt->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
802 #else
803     return false;
804 #endif
805 }
806
807 bool HitTestResult::mediaIsInEnhancedFullscreen() const
808 {
809 #if PLATFORM(MAC) && ENABLE(VIDEO) && ENABLE(VIDEO_PRESENTATION_MODE)
810     HTMLMediaElement* mediaElt(mediaElement());
811     return is<HTMLVideoElement>(mediaElt) && mediaElt->fullscreenMode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture;
812 #else
813     return false;
814 #endif
815 }
816
817 void HitTestResult::toggleEnhancedFullscreenForVideo() const
818 {
819 #if PLATFORM(MAC) && ENABLE(VIDEO) && ENABLE(VIDEO_PRESENTATION_MODE)
820     HTMLMediaElement* mediaElement(this->mediaElement());
821     if (!is<HTMLVideoElement>(mediaElement) || !mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture))
822         return;
823
824     HTMLVideoElement& videoElement = downcast<HTMLVideoElement>(*mediaElement);
825     UserGestureIndicator indicator(ProcessingUserGesture, &mediaElement->document());
826     if (videoElement.fullscreenMode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)
827         videoElement.exitFullscreen();
828     else
829         videoElement.enterFullscreen(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
830 #endif
831 }
832
833 } // namespace WebCore