summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/HitTestResult.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/HitTestResult.cpp')
-rw-r--r--Source/WebCore/rendering/HitTestResult.cpp378
1 files changed, 249 insertions, 129 deletions
diff --git a/Source/WebCore/rendering/HitTestResult.cpp b/Source/WebCore/rendering/HitTestResult.cpp
index aa4a699b4..d22585a03 100644
--- a/Source/WebCore/rendering/HitTestResult.cpp
+++ b/Source/WebCore/rendering/HitTestResult.cpp
@@ -25,16 +25,18 @@
#include "CachedImage.h"
#include "DocumentMarkerController.h"
#include "Editor.h"
+#include "File.h"
#include "Frame.h"
#include "FrameSelection.h"
#include "FrameTree.h"
#include "HTMLAnchorElement.h"
-#include "HTMLAreaElement.h"
-#include "HTMLAudioElement.h"
+#include "HTMLAttachmentElement.h"
+#include "HTMLEmbedElement.h"
#include "HTMLImageElement.h"
#include "HTMLInputElement.h"
#include "HTMLMediaElement.h"
#include "HTMLNames.h"
+#include "HTMLObjectElement.h"
#include "HTMLParserIdioms.h"
#include "HTMLPlugInImageElement.h"
#include "HTMLTextAreaElement.h"
@@ -44,15 +46,14 @@
#include "RenderBlockFlow.h"
#include "RenderImage.h"
#include "RenderInline.h"
+#include "SVGAElement.h"
+#include "SVGImageElement.h"
#include "Scrollbar.h"
#include "ShadowRoot.h"
+#include "TextIterator.h"
#include "UserGestureIndicator.h"
-
-#if ENABLE(SVG)
-#include "SVGImageElement.h"
-#include "SVGNames.h"
+#include "VisibleUnits.h"
#include "XLinkNames.h"
-#endif
namespace WebCore {
@@ -95,7 +96,7 @@ HitTestResult::HitTestResult(const HitTestResult& other)
, m_isOverWidget(other.isOverWidget())
{
// Only copy the NodeSet in case of rect hit test.
- m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
+ m_rectBasedTestResult = other.m_rectBasedTestResult ? std::make_unique<NodeSet>(*other.m_rectBasedTestResult) : nullptr;
}
HitTestResult::~HitTestResult()
@@ -114,35 +115,46 @@ HitTestResult& HitTestResult::operator=(const HitTestResult& other)
m_isOverWidget = other.isOverWidget();
// Only copy the NodeSet in case of rect hit test.
- m_rectBasedTestResult = adoptPtr(other.m_rectBasedTestResult ? new NodeSet(*other.m_rectBasedTestResult) : 0);
+ m_rectBasedTestResult = other.m_rectBasedTestResult ? std::make_unique<NodeSet>(*other.m_rectBasedTestResult) : nullptr;
return *this;
}
-void HitTestResult::setToNonShadowAncestor()
+static Node* moveOutOfUserAgentShadowTree(Node& node)
{
- Node* node = innerNode();
- if (node)
- node = node->document().ancestorInThisScope(node);
- setInnerNode(node);
- node = innerNonSharedNode();
- if (node)
- node = node->document().ancestorInThisScope(node);
- setInnerNonSharedNode(node);
+ if (node.isInShadowTree()) {
+ if (ShadowRoot* root = node.containingShadowRoot()) {
+ if (root->mode() == ShadowRootMode::UserAgent)
+ return root->host();
+ }
+ }
+ return &node;
}
-void HitTestResult::setInnerNode(Node* n)
+void HitTestResult::setToNonUserAgentShadowAncestor()
{
- if (n && n->isPseudoElement())
- n = toPseudoElement(n)->hostElement();
- m_innerNode = n;
+ if (Node* node = innerNode()) {
+ node = moveOutOfUserAgentShadowTree(*node);
+ setInnerNode(node);
+ }
+ if (Node *node = innerNonSharedNode()) {
+ node = moveOutOfUserAgentShadowTree(*node);
+ setInnerNonSharedNode(node);
+ }
+}
+
+void HitTestResult::setInnerNode(Node* node)
+{
+ if (is<PseudoElement>(node))
+ node = downcast<PseudoElement>(*node).hostElement();
+ m_innerNode = node;
}
-void HitTestResult::setInnerNonSharedNode(Node* n)
+void HitTestResult::setInnerNonSharedNode(Node* node)
{
- if (n && n->isPseudoElement())
- n = toPseudoElement(n)->hostElement();
- m_innerNonSharedNode = n;
+ if (is<PseudoElement>(node))
+ node = downcast<PseudoElement>(*node).hostElement();
+ m_innerNonSharedNode = node;
}
void HitTestResult::setURLElement(Element* n)
@@ -188,6 +200,26 @@ bool HitTestResult::isSelected() const
return frame->selection().contains(m_hitTestLocation.point());
}
+String HitTestResult::selectedText() const
+{
+ if (!m_innerNonSharedNode)
+ return emptyString();
+
+ Frame* frame = m_innerNonSharedNode->document().frame();
+ if (!frame)
+ return emptyString();
+
+ // Look for a character that's not just a separator.
+ for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) {
+ int length = it.text().length();
+ for (int i = 0; i < length; ++i) {
+ if (!(U_GET_GC_MASK(it.text()[i]) & U_GC_Z_MASK))
+ return frame->displayStringModifiedByEncoding(frame->editor().selectedText());
+ }
+ }
+ return emptyString();
+}
+
String HitTestResult::spellingToolTip(TextDirection& dir) const
{
dir = LTR;
@@ -224,11 +256,12 @@ String HitTestResult::title(TextDirection& dir) const
dir = LTR;
// Find the title in the nearest enclosing DOM node.
// For <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
- for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
- if (titleNode->isElementNode()) {
- String title = toElement(titleNode)->title();
+ for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentInComposedTree()) {
+ if (is<Element>(*titleNode)) {
+ Element& titleElement = downcast<Element>(*titleNode);
+ String title = titleElement.title();
if (!title.isEmpty()) {
- if (auto renderer = titleNode->renderer())
+ if (auto renderer = titleElement.renderer())
dir = renderer->style().direction();
return title;
}
@@ -239,18 +272,18 @@ String HitTestResult::title(TextDirection& dir) const
String HitTestResult::innerTextIfTruncated(TextDirection& dir) const
{
- for (Node* truncatedNode = m_innerNode.get(); truncatedNode; truncatedNode = truncatedNode->parentNode()) {
- if (!truncatedNode->isElementNode())
+ for (Node* truncatedNode = m_innerNode.get(); truncatedNode; truncatedNode = truncatedNode->parentInComposedTree()) {
+ if (!is<Element>(*truncatedNode))
continue;
- if (auto renderer = toElement(truncatedNode)->renderer()) {
- if (renderer->isRenderBlockFlow()) {
- RenderBlockFlow* block = toRenderBlockFlow(renderer);
- if (block->style().textOverflow()) {
- for (RootInlineBox* line = block->firstRootBox(); line; line = line->nextRootBox()) {
+ if (auto renderer = downcast<Element>(*truncatedNode).renderer()) {
+ if (is<RenderBlockFlow>(*renderer)) {
+ RenderBlockFlow& block = downcast<RenderBlockFlow>(*renderer);
+ if (block.style().textOverflow()) {
+ for (RootInlineBox* line = block.firstRootBox(); line; line = line->nextRootBox()) {
if (line->hasEllipsisBox()) {
- dir = block->style().direction();
- return toElement(truncatedNode)->innerText();
+ dir = block.style().direction();
+ return downcast<Element>(*truncatedNode).innerText();
}
}
}
@@ -275,14 +308,14 @@ String HitTestResult::altDisplayString() const
if (!m_innerNonSharedNode)
return String();
- if (isHTMLImageElement(m_innerNonSharedNode.get())) {
- HTMLImageElement* image = toHTMLImageElement(m_innerNonSharedNode.get());
- return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get());
+ if (is<HTMLImageElement>(*m_innerNonSharedNode)) {
+ HTMLImageElement& image = downcast<HTMLImageElement>(*m_innerNonSharedNode);
+ return displayString(image.attributeWithoutSynchronization(altAttr), m_innerNonSharedNode.get());
}
- if (isHTMLInputElement(m_innerNonSharedNode.get())) {
- HTMLInputElement* input = toHTMLInputElement(m_innerNonSharedNode.get());
- return displayString(input->alt(), m_innerNonSharedNode.get());
+ if (is<HTMLInputElement>(*m_innerNonSharedNode)) {
+ HTMLInputElement& input = downcast<HTMLInputElement>(*m_innerNonSharedNode);
+ return displayString(input.alt(), m_innerNonSharedNode.get());
}
return String();
@@ -291,16 +324,16 @@ String HitTestResult::altDisplayString() const
Image* HitTestResult::image() const
{
if (!m_innerNonSharedNode)
- return 0;
+ return nullptr;
- auto renderer = m_innerNonSharedNode->renderer();
- if (renderer && renderer->isRenderImage()) {
- RenderImage* image = toRenderImage(renderer);
- if (image->cachedImage() && !image->cachedImage()->errorOccurred())
- return image->cachedImage()->imageForRenderer(image);
+ auto* renderer = m_innerNonSharedNode->renderer();
+ if (is<RenderImage>(renderer)) {
+ auto& image = downcast<RenderImage>(*renderer);
+ if (image.cachedImage() && !image.cachedImage()->errorOccurred())
+ return image.cachedImage()->imageForRenderer(&image);
}
- return 0;
+ return nullptr;
}
IntRect HitTestResult::imageRect() const
@@ -310,6 +343,25 @@ IntRect HitTestResult::imageRect() const
return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
}
+#if ENABLE(ATTACHMENT_ELEMENT)
+URL HitTestResult::absoluteAttachmentURL() const
+{
+ if (!m_innerNonSharedNode)
+ return URL();
+
+ if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isAttachment()))
+ return URL();
+
+ if (!is<HTMLAttachmentElement>(*m_innerNonSharedNode))
+ return URL();
+ File* attachmentFile = downcast<HTMLAttachmentElement>(*m_innerNonSharedNode).file();
+ if (!attachmentFile)
+ return URL();
+
+ return URL::fileURLWithFileSystemPath(attachmentFile->path());
+}
+#endif
+
URL HitTestResult::absoluteImageURL() const
{
if (!m_innerNonSharedNode)
@@ -319,16 +371,12 @@ URL HitTestResult::absoluteImageURL() const
return URL();
AtomicString urlString;
- if (m_innerNonSharedNode->hasTagName(embedTag)
- || isHTMLImageElement(m_innerNonSharedNode.get())
- || isHTMLInputElement(m_innerNonSharedNode.get())
- || m_innerNonSharedNode->hasTagName(objectTag)
-#if ENABLE(SVG)
- || isSVGImageElement(m_innerNonSharedNode.get())
-#endif
- ) {
- Element* element = toElement(m_innerNonSharedNode.get());
- urlString = element->imageSourceURL();
+ if (is<HTMLEmbedElement>(*m_innerNonSharedNode)
+ || is<HTMLImageElement>(*m_innerNonSharedNode)
+ || is<HTMLInputElement>(*m_innerNonSharedNode)
+ || is<HTMLObjectElement>(*m_innerNonSharedNode)
+ || is<SVGImageElement>(*m_innerNonSharedNode)) {
+ urlString = downcast<Element>(*m_innerNonSharedNode).imageSourceURL();
} else
return URL();
@@ -340,15 +388,15 @@ URL HitTestResult::absolutePDFURL() const
if (!m_innerNonSharedNode)
return URL();
- if (!m_innerNonSharedNode->hasTagName(embedTag) && !m_innerNonSharedNode->hasTagName(objectTag))
+ if (!is<HTMLEmbedElement>(*m_innerNonSharedNode) && !is<HTMLObjectElement>(*m_innerNonSharedNode))
return URL();
- HTMLPlugInImageElement* element = toHTMLPlugInImageElement(m_innerNonSharedNode.get());
- URL url = m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(element->url()));
+ HTMLPlugInImageElement& element = downcast<HTMLPlugInImageElement>(*m_innerNonSharedNode);
+ URL url = m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(element.url()));
if (!url.isValid())
return URL();
- if (element->serviceType() == "application/pdf" || (element->serviceType().isEmpty() && url.path().lower().endsWith(".pdf")))
+ if (element.serviceType() == "application/pdf" || (element.serviceType().isEmpty() && url.path().endsWith(".pdf", false)))
return url;
return URL();
}
@@ -368,7 +416,7 @@ bool HitTestResult::mediaSupportsFullscreen() const
{
#if ENABLE(VIDEO)
HTMLMediaElement* mediaElt(mediaElement());
- return (mediaElt && isHTMLVideoElement(mediaElt) && mediaElt->supportsFullscreen());
+ return is<HTMLVideoElement>(mediaElt) && mediaElt->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard);
#else
return false;
#endif
@@ -378,14 +426,14 @@ bool HitTestResult::mediaSupportsFullscreen() const
HTMLMediaElement* HitTestResult::mediaElement() const
{
if (!m_innerNonSharedNode)
- return 0;
+ return nullptr;
if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
- return 0;
+ return nullptr;
- if (isHTMLVideoElement(m_innerNonSharedNode.get()) || isHTMLAudioElement(m_innerNonSharedNode.get()))
- return toHTMLMediaElement(m_innerNonSharedNode.get());
- return 0;
+ if (is<HTMLMediaElement>(*m_innerNonSharedNode))
+ return downcast<HTMLMediaElement>(m_innerNonSharedNode.get());
+ return nullptr;
}
#endif
@@ -409,7 +457,7 @@ bool HitTestResult::mediaIsInFullscreen() const
{
#if ENABLE(VIDEO)
if (HTMLMediaElement* mediaElement = this->mediaElement())
- return mediaElement->isVideo() && mediaElement->isFullscreen();
+ return mediaElement->isVideo() && mediaElement->isStandardFullscreen();
#endif
return false;
}
@@ -418,9 +466,9 @@ void HitTestResult::toggleMediaFullscreenState() const
{
#if ENABLE(VIDEO)
if (HTMLMediaElement* mediaElement = this->mediaElement()) {
- if (mediaElement->isVideo() && mediaElement->supportsFullscreen()) {
- UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
- mediaElement->toggleFullscreenState();
+ if (mediaElement->isVideo() && mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
+ UserGestureIndicator indicator(ProcessingUserGesture, &mediaElement->document());
+ mediaElement->toggleStandardFullscreenState();
}
}
#endif
@@ -429,12 +477,12 @@ void HitTestResult::toggleMediaFullscreenState() const
void HitTestResult::enterFullscreenForVideo() const
{
#if ENABLE(VIDEO)
- HTMLMediaElement* mediaElt(mediaElement());
- if (mediaElt && isHTMLVideoElement(mediaElt)) {
- HTMLVideoElement* videoElt = toHTMLVideoElement(mediaElt);
- if (!videoElt->isFullscreen() && mediaElt->supportsFullscreen()) {
- UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
- videoElt->enterFullscreen();
+ HTMLMediaElement* mediaElement(this->mediaElement());
+ if (is<HTMLVideoElement>(mediaElement)) {
+ HTMLVideoElement& videoElement = downcast<HTMLVideoElement>(*mediaElement);
+ if (!videoElement.isFullscreen() && mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
+ UserGestureIndicator indicator(ProcessingUserGesture, &mediaElement->document());
+ videoElement.enterFullscreen();
}
}
#endif
@@ -443,8 +491,8 @@ void HitTestResult::enterFullscreenForVideo() const
bool HitTestResult::mediaControlsEnabled() const
{
#if ENABLE(VIDEO)
- if (HTMLMediaElement* mediaElt = mediaElement())
- return mediaElt->controls();
+ if (HTMLMediaElement* mediaElement = this->mediaElement())
+ return mediaElement->controls();
#endif
return false;
}
@@ -488,7 +536,7 @@ bool HitTestResult::mediaIsVideo() const
{
#if ENABLE(VIDEO)
if (HTMLMediaElement* mediaElt = mediaElement())
- return isHTMLVideoElement(mediaElt);
+ return is<HTMLVideoElement>(*mediaElt);
#endif
return false;
}
@@ -510,37 +558,64 @@ void HitTestResult::toggleMediaMuteState() const
#endif
}
-URL HitTestResult::absoluteLinkURL() const
+bool HitTestResult::isDownloadableMedia() const
{
- if (!m_innerURLElement)
- return URL();
-
- AtomicString urlString;
- if (isHTMLAnchorElement(m_innerURLElement.get()) || isHTMLAreaElement(m_innerURLElement.get()) || m_innerURLElement->hasTagName(linkTag))
- urlString = m_innerURLElement->getAttribute(hrefAttr);
-#if ENABLE(SVG)
- else if (m_innerURLElement->hasTagName(SVGNames::aTag))
- urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr);
+#if ENABLE(VIDEO)
+ if (HTMLMediaElement* mediaElt = mediaElement())
+ return mediaElt->canSaveMediaData();
#endif
- else
- return URL();
- return m_innerURLElement->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
+ return false;
}
-bool HitTestResult::isLiveLink() const
+bool HitTestResult::isOverTextInsideFormControlElement() const
{
- if (!m_innerURLElement)
+ Node* node = innerNode();
+ if (!node)
return false;
- if (isHTMLAnchorElement(m_innerURLElement.get()))
- return toHTMLAnchorElement(m_innerURLElement.get())->isLiveLink();
-#if ENABLE(SVG)
- if (m_innerURLElement->hasTagName(SVGNames::aTag))
- return m_innerURLElement->isLink();
-#endif
+ if (!is<HTMLTextFormControlElement>(*node))
+ return false;
- return false;
+ Frame* frame = node->document().frame();
+ if (!frame)
+ return false;
+
+ IntPoint framePoint = roundedPointInInnerNodeFrame();
+ if (!frame->rangeForPoint(framePoint))
+ return false;
+
+ VisiblePosition position = frame->visiblePositionForPoint(framePoint);
+ if (position.isNull())
+ return false;
+
+ RefPtr<Range> wordRange = enclosingTextUnitOfGranularity(position, WordGranularity, DirectionForward);
+ if (!wordRange)
+ return false;
+
+ return !wordRange->text().isEmpty();
+}
+
+bool HitTestResult::allowsCopy() const
+{
+ Node* node = innerNode();
+ if (!node)
+ return false;
+
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ return false;
+
+ bool isUserSelectNone = renderer->style().userSelect() == SELECT_NONE;
+ bool isPasswordField = is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).isPasswordField();
+ return !isPasswordField && !isUserSelectNone;
+}
+
+URL HitTestResult::absoluteLinkURL() const
+{
+ if (m_innerURLElement)
+ return m_innerURLElement->absoluteLinkURL();
+ return URL();
}
bool HitTestResult::isOverLink() const
@@ -572,11 +647,11 @@ bool HitTestResult::isContentEditable() const
if (!m_innerNonSharedNode)
return false;
- if (isHTMLTextAreaElement(m_innerNonSharedNode.get()))
+ if (is<HTMLTextAreaElement>(*m_innerNonSharedNode))
return true;
- if (isHTMLInputElement(m_innerNonSharedNode.get()))
- return toHTMLInputElement(m_innerNonSharedNode.get())->isTextField();
+ if (is<HTMLInputElement>(*m_innerNonSharedNode))
+ return downcast<HTMLInputElement>(*m_innerNonSharedNode).isTextField();
return m_innerNonSharedNode->hasEditableStyle();
}
@@ -592,8 +667,9 @@ bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestReques
if (!node)
return true;
- if (request.disallowsShadowContent())
- node = node->document().ancestorInThisScope(node);
+ // FIXME: This moves out of a author shadow tree.
+ if (request.disallowsUserAgentShadowContent())
+ node = node->document().ancestorNodeInThisScope(node);
mutableRectBasedTestResult().add(node);
@@ -612,8 +688,9 @@ bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestReques
if (!node)
return true;
- if (request.disallowsShadowContent())
- node = node->document().ancestorInThisScope(node);
+ // FIXME: This moves out of a author shadow tree.
+ if (request.disallowsUserAgentShadowContent())
+ node = node->document().ancestorNodeInThisScope(node);
mutableRectBasedTestResult().add(node);
@@ -645,14 +722,14 @@ void HitTestResult::append(const HitTestResult& other)
const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
{
if (!m_rectBasedTestResult)
- m_rectBasedTestResult = adoptPtr(new NodeSet);
+ m_rectBasedTestResult = std::make_unique<NodeSet>();
return *m_rectBasedTestResult;
}
HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
{
if (!m_rectBasedTestResult)
- m_rectBasedTestResult = adoptPtr(new NodeSet);
+ m_rectBasedTestResult = std::make_unique<NodeSet>();
return *m_rectBasedTestResult;
}
@@ -670,7 +747,7 @@ Vector<String> HitTestResult::dictationAlternatives() const
if (!frame)
return Vector<String>();
- return frame->editor().dictationAlternativesForMarker(marker);
+ return frame->editor().dictationAlternativesForMarker(*marker);
}
Node* HitTestResult::targetNode() const
@@ -678,34 +755,77 @@ Node* HitTestResult::targetNode() const
Node* node = innerNode();
if (!node)
return 0;
- if (node->inDocument())
+ if (node->isConnected())
return node;
Element* element = node->parentElement();
- if (element && element->inDocument())
+ if (element && element->isConnected())
return element;
return node;
}
-Element* HitTestResult::innerElement() const
+Element* HitTestResult::targetElement() const
{
- Node* node = m_innerNode.get();
- if (!node)
- return 0;
- if (node->isElementNode())
- return toElement(node);
- return node->parentElement();
+ for (Node* node = m_innerNode.get(); node; node = node->parentInComposedTree()) {
+ if (is<Element>(*node))
+ return downcast<Element>(node);
+ }
+ return nullptr;
}
Element* HitTestResult::innerNonSharedElement() const
{
Node* node = m_innerNonSharedNode.get();
if (!node)
- return 0;
- if (node->isElementNode())
- return toElement(node);
+ return nullptr;
+ if (is<Element>(*node))
+ return downcast<Element>(node);
return node->parentElement();
}
+const AtomicString& HitTestResult::URLElementDownloadAttribute() const
+{
+ auto* urlElement = URLElement();
+ if (!is<HTMLAnchorElement>(urlElement))
+ return nullAtom;
+ return urlElement->attributeWithoutSynchronization(HTMLNames::downloadAttr);
+}
+
+bool HitTestResult::mediaSupportsEnhancedFullscreen() const
+{
+#if PLATFORM(MAC) && ENABLE(VIDEO) && ENABLE(VIDEO_PRESENTATION_MODE)
+ HTMLMediaElement* mediaElt(mediaElement());
+ return is<HTMLVideoElement>(mediaElt) && mediaElt->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
+#else
+ return false;
+#endif
+}
+
+bool HitTestResult::mediaIsInEnhancedFullscreen() const
+{
+#if PLATFORM(MAC) && ENABLE(VIDEO) && ENABLE(VIDEO_PRESENTATION_MODE)
+ HTMLMediaElement* mediaElt(mediaElement());
+ return is<HTMLVideoElement>(mediaElt) && mediaElt->fullscreenMode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture;
+#else
+ return false;
+#endif
+}
+
+void HitTestResult::toggleEnhancedFullscreenForVideo() const
+{
+#if PLATFORM(MAC) && ENABLE(VIDEO) && ENABLE(VIDEO_PRESENTATION_MODE)
+ HTMLMediaElement* mediaElement(this->mediaElement());
+ if (!is<HTMLVideoElement>(mediaElement) || !mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture))
+ return;
+
+ HTMLVideoElement& videoElement = downcast<HTMLVideoElement>(*mediaElement);
+ UserGestureIndicator indicator(ProcessingUserGesture, &mediaElement->document());
+ if (videoElement.fullscreenMode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)
+ videoElement.exitFullscreen();
+ else
+ videoElement.enterFullscreen(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
+#endif
+}
+
} // namespace WebCore