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