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