Use a 1-byte enum class for TextDirection
[WebKit-https.git] / Source / WebCore / rendering / HitTestResult.cpp
index 0e2f2ba..748eae3 100644 (file)
 
 #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 "HTMLVideoElement.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 "RenderBlock.h"
+#include "HTMLTextAreaElement.h"
+#include "HTMLVideoElement.h"
+#include "HitTestLocation.h"
+#include "PseudoElement.h"
+#include "RenderBlockFlow.h"
 #include "RenderImage.h"
 #include "RenderInline.h"
+#include "SVGAElement.h"
+#include "SVGImageElement.h"
 #include "Scrollbar.h"
-
-#if ENABLE(SVG)
-#include "SVGNames.h"
+#include "ShadowRoot.h"
+#include "TextIterator.h"
+#include "UserGestureIndicator.h"
+#include "VisibleUnits.h"
 #include "XLinkNames.h"
-#endif
 
 namespace WebCore {
 
 using namespace HTMLNames;
 
-HitTestLocation::HitTestLocation()
-    : m_region(0)
-    , m_isRectBased(false)
-    , m_isRectilinear(true)
-{
-}
-
-HitTestLocation::HitTestLocation(const LayoutPoint& point)
-    : m_point(point)
-    , m_boundingBox(rectForPoint(point, 0, 0, 0, 0))
-    , m_transformedPoint(point)
-    , m_transformedRect(m_boundingBox)
-    , m_region(0)
-    , m_isRectBased(false)
-    , m_isRectilinear(true)
-{
-}
-
-HitTestLocation::HitTestLocation(const FloatPoint& point)
-    : m_point(flooredLayoutPoint(point))
-    , m_boundingBox(rectForPoint(m_point, 0, 0, 0, 0))
-    , m_transformedPoint(point)
-    , m_transformedRect(m_boundingBox)
-    , m_region(0)
-    , m_isRectBased(false)
-    , m_isRectilinear(true)
-{
-}
-
-HitTestLocation::HitTestLocation(const FloatPoint& point, const FloatQuad& quad)
-    : m_transformedPoint(point)
-    , m_transformedRect(quad)
-    , m_region(0)
-    , m_isRectBased(true)
-{
-    m_point = flooredLayoutPoint(point);
-    m_boundingBox = enclosingIntRect(quad.boundingBox());
-    m_isRectilinear = quad.isRectilinear();
-}
-
-HitTestLocation::HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
-    : m_point(centerPoint)
-    , m_boundingBox(rectForPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding))
-    , m_transformedPoint(centerPoint)
-    , m_region(0)
-    , m_isRectBased(topPadding || rightPadding || bottomPadding || leftPadding)
-    , m_isRectilinear(true)
-{
-    m_transformedRect = FloatQuad(m_boundingBox);
-}
-
-HitTestLocation::HitTestLocation(const HitTestLocation& other, const LayoutSize& offset, RenderRegion* region)
-    : m_point(other.m_point)
-    , m_boundingBox(other.m_boundingBox)
-    , m_transformedPoint(other.m_transformedPoint)
-    , m_transformedRect(other.m_transformedRect)
-    , m_region(region ? region : other.m_region)
-    , m_isRectBased(other.m_isRectBased)
-    , m_isRectilinear(other.m_isRectilinear)
-{
-    move(offset);
-}
-
-HitTestLocation::HitTestLocation(const HitTestLocation& other)
-    : m_point(other.m_point)
-    , m_boundingBox(other.m_boundingBox)
-    , m_transformedPoint(other.m_transformedPoint)
-    , m_transformedRect(other.m_transformedRect)
-    , m_region(other.m_region)
-    , m_isRectBased(other.m_isRectBased)
-    , m_isRectilinear(other.m_isRectilinear)
-{
-}
-
-HitTestLocation::~HitTestLocation()
-{
-}
-
-HitTestLocation& HitTestLocation::operator=(const HitTestLocation& other)
-{
-    m_point = other.m_point;
-    m_boundingBox = other.m_boundingBox;
-    m_transformedPoint = other.m_transformedPoint;
-    m_transformedRect = other.m_transformedRect;
-    m_region = other.m_region;
-    m_isRectBased = other.m_isRectBased;
-    m_isRectilinear = other.m_isRectilinear;
-
-    return *this;
-}
-
-void HitTestLocation::move(const LayoutSize& offset)
-{
-    m_point.move(offset);
-    m_transformedPoint.move(offset);
-    m_transformedRect.move(offset);
-    m_boundingBox = enclosingIntRect(m_transformedRect.boundingBox());
-}
-
-template<typename RectType>
-bool HitTestLocation::intersectsRect(const RectType& rect) const
-{
-    // FIXME: When the hit test is not rect based we should use rect.contains(m_point).
-    // That does change some corner case tests though.
-
-    // First check if rect even intersects our bounding box.
-    if (!rect.intersects(m_boundingBox))
-        return false;
-
-    // If the transformed rect is rectilinear the bounding box intersection was accurate.
-    if (m_isRectilinear)
-        return true;
-
-    // If rect fully contains our bounding box, we are also sure of an intersection.
-    if (rect.contains(m_boundingBox))
-        return true;
-
-    // Otherwise we need to do a slower quad based intersection test.
-    return m_transformedRect.intersectsRect(rect);
-}
-
-bool HitTestLocation::intersects(const LayoutRect& rect) const
-{
-    return intersectsRect(rect);
-}
-
-bool HitTestLocation::intersects(const FloatRect& rect) const
-{
-    return intersectsRect(rect);
-}
-
-IntRect HitTestLocation::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
-{
-    IntPoint actualPoint(flooredIntPoint(point));
-    actualPoint -= IntSize(leftPadding, topPadding);
-
-    IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding);
-    // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1".
-    // FIXME: Remove this once non-rect based hit-detection stops using IntRect:intersects.
-    actualPadding += IntSize(1, 1);
-
-    return IntRect(actualPoint, actualPadding);
-}
-
 HitTestResult::HitTestResult()
     : m_isOverWidget(false)
 {
@@ -229,13 +95,11 @@ HitTestResult::HitTestResult(const HitTestResult& other)
     , m_scrollbar(other.scrollbar())
     , 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);
+    // Only copy the NodeSet in case of list hit test.
+    m_listBasedTestResult = other.m_listBasedTestResult ? std::make_unique<NodeSet>(*other.m_listBasedTestResult) : nullptr;
 }
 
-HitTestResult::~HitTestResult()
-{
-}
+HitTestResult::~HitTestResult() = default;
 
 HitTestResult& HitTestResult::operator=(const HitTestResult& other)
 {
@@ -248,36 +112,47 @@ HitTestResult& HitTestResult::operator=(const HitTestResult& other)
     m_scrollbar = other.scrollbar();
     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);
+    // Only copy the NodeSet in case of list hit test.
+    m_listBasedTestResult = other.m_listBasedTestResult ? std::make_unique<NodeSet>(*other.m_listBasedTestResult) : nullptr;
 
     return *this;
 }
 
-void HitTestResult::setToNonShadowAncestor()
+static Node* moveOutOfUserAgentShadowTree(Node& node)
 {
-    Node* node = innerNode();
-    if (node)
-        node = node->deprecatedShadowAncestorNode();
-    setInnerNode(node);
-    node = innerNonSharedNode();
-    if (node)
-        node = node->deprecatedShadowAncestorNode();
-    setInnerNonSharedNode(node);
+    if (node.isInShadowTree()) {
+        if (ShadowRoot* root = node.containingShadowRoot()) {
+            if (root->mode() == ShadowRootMode::UserAgent)
+                return root->host();
+        }
+    }
+    return &node;
+}
+
+void HitTestResult::setToNonUserAgentShadowAncestor()
+{
+    if (Node* node = innerNode()) {
+        node = moveOutOfUserAgentShadowTree(*node);
+        setInnerNode(node);
+    }
+    if (Node *node = innerNonSharedNode()) {
+        node = moveOutOfUserAgentShadowTree(*node);
+        setInnerNonSharedNode(node);
+    }
 }
 
-void HitTestResult::setInnerNode(Node* n)
+void HitTestResult::setInnerNode(Node* node)
 {
-    if (n && n->isPseudoElement())
-        n = n->parentOrShadowHostNode();
-    m_innerNode = n;
+    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 = n->parentOrShadowHostNode();
-    m_innerNonSharedNode = n;
+    if (is<PseudoElement>(node))
+        node = downcast<PseudoElement>(*node).hostElement();
+    m_innerNonSharedNode = node;
 }
 
 void HitTestResult::setURLElement(Element* n) 
@@ -293,9 +168,9 @@ void HitTestResult::setScrollbar(Scrollbar* s)
 Frame* HitTestResult::innerNodeFrame() const
 {
     if (m_innerNonSharedNode)
-        return m_innerNonSharedNode->document()->frame();
+        return m_innerNonSharedNode->document().frame();
     if (m_innerNode)
-        return m_innerNode->document()->frame();
+        return m_innerNode->document().frame();
     return 0;
 }
 
@@ -304,11 +179,11 @@ Frame* HitTestResult::targetFrame() const
     if (!m_innerURLElement)
         return 0;
 
-    Frame* frame = m_innerURLElement->document()->frame();
+    Frame* frame = m_innerURLElement->document().frame();
     if (!frame)
         return 0;
 
-    return frame->tree()->find(m_innerURLElement->target());
+    return frame->tree().find(m_innerURLElement->target());
 }
 
 bool HitTestResult::isSelected() const
@@ -316,27 +191,47 @@ bool HitTestResult::isSelected() const
     if (!m_innerNonSharedNode)
         return false;
 
-    Frame* frame = m_innerNonSharedNode->document()->frame();
+    Frame* frame = m_innerNonSharedNode->document().frame();
     if (!frame)
         return false;
 
-    return frame->selection()->contains(m_hitTestLocation.point());
+    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;
+    dir = TextDirection::LTR;
     // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar
     // currently supply strings, but maybe someday markers associated with misspelled words will also.
     if (!m_innerNonSharedNode)
         return String();
     
-    DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Grammar);
+    DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Grammar);
     if (!marker)
         return String();
 
-    if (RenderObject* renderer = m_innerNonSharedNode->renderer())
-        dir = renderer->style()->direction();
+    if (auto renderer = m_innerNonSharedNode->renderer())
+        dir = renderer->style().direction();
     return marker->description();
 }
 
@@ -347,7 +242,7 @@ String HitTestResult::replacedString() const
     if (!m_innerNonSharedNode)
         return String();
     
-    DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Replacement);
+    DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(m_hitTestLocation.point(), DocumentMarker::Replacement);
     if (!marker)
         return String();
     
@@ -356,15 +251,16 @@ String HitTestResult::replacedString() const
     
 String HitTestResult::title(TextDirection& dir) const
 {
-    dir = LTR;
+    dir = TextDirection::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 = static_cast<Element*>(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 (RenderObject* renderer = titleNode->renderer())
-                    dir = renderer->style()->direction();
+                if (auto renderer = titleElement.renderer())
+                    dir = renderer->style().direction();
                 return title;
             }
         }
@@ -374,18 +270,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 (RenderObject* renderer = truncatedNode->renderer()) {
-            if (renderer->isRenderBlock()) {
-                RenderBlock* block = toRenderBlock(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() == TextOverflow::Ellipsis) {
+                    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();
                         }
                     }
                 }
@@ -394,7 +290,7 @@ String HitTestResult::innerTextIfTruncated(TextDirection& dir) const
         }
     }
 
-    dir = LTR;
+    dir = TextDirection::LTR;
     return String();
 }
 
@@ -402,7 +298,7 @@ String displayString(const String& string, const Node* node)
 {
     if (!node)
         return string;
-    return node->document()->displayStringModifiedByEncoding(string);
+    return node->document().displayStringModifiedByEncoding(string);
 }
 
 String HitTestResult::altDisplayString() const
@@ -410,14 +306,14 @@ String HitTestResult::altDisplayString() const
     if (!m_innerNonSharedNode)
         return String();
     
-    if (m_innerNonSharedNode->hasTagName(imgTag)) {
-        HTMLImageElement* image = static_cast<HTMLImageElement*>(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 (m_innerNonSharedNode->hasTagName(inputTag)) {
-        HTMLInputElement* input = static_cast<HTMLInputElement*>(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();
@@ -426,16 +322,16 @@ String HitTestResult::altDisplayString() const
 Image* HitTestResult::image() const
 {
     if (!m_innerNonSharedNode)
-        return 0;
+        return nullptr;
     
-    RenderObject* renderer = m_innerNonSharedNode->renderer();
-    if (renderer && renderer->isImage()) {
-        RenderImage* image = static_cast<WebCore::RenderImage*>(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
@@ -445,57 +341,53 @@ IntRect HitTestResult::imageRect() const
     return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox();
 }
 
-KURL HitTestResult::absoluteImageURL() const
+URL HitTestResult::absoluteImageURL() const
 {
-    if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
-        return KURL();
+    if (!m_innerNonSharedNode)
+        return URL();
 
     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage()))
-        return KURL();
+        return URL();
 
     AtomicString urlString;
-    if (m_innerNonSharedNode->hasTagName(embedTag)
-        || m_innerNonSharedNode->hasTagName(imgTag)
-        || m_innerNonSharedNode->hasTagName(inputTag)
-        || m_innerNonSharedNode->hasTagName(objectTag)    
-#if ENABLE(SVG)
-        || m_innerNonSharedNode->hasTagName(SVGNames::imageTag)
-#endif
-       ) {
-        Element* element = static_cast<Element*>(m_innerNonSharedNode.get());
-        urlString = element->getAttribute(element->imageSourceAttributeName());
+    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 KURL();
+        return URL();
 
-    return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
+    return m_innerNonSharedNode->document().completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
 }
 
-KURL HitTestResult::absolutePDFURL() const
+URL HitTestResult::absolutePDFURL() const
 {
-    if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
-        return KURL();
+    if (!m_innerNonSharedNode)
+        return URL();
 
-    if (!m_innerNonSharedNode->hasTagName(embedTag) && !m_innerNonSharedNode->hasTagName(objectTag))
-        return KURL();
+    if (!is<HTMLEmbedElement>(*m_innerNonSharedNode) && !is<HTMLObjectElement>(*m_innerNonSharedNode))
+        return URL();
 
-    HTMLPlugInImageElement* element = static_cast<HTMLPlugInImageElement*>(m_innerNonSharedNode.get());
-    KURL 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 KURL();
+        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().endsWithIgnoringASCIICase(".pdf")))
         return url;
-    return KURL();
+    return URL();
 }
 
-KURL HitTestResult::absoluteMediaURL() const
+URL HitTestResult::absoluteMediaURL() const
 {
 #if ENABLE(VIDEO)
     if (HTMLMediaElement* mediaElt = mediaElement())
         return mediaElt->currentSrc();
-    return KURL();
+    return URL();
 #else
-    return KURL();
+    return URL();
 #endif
 }
 
@@ -503,7 +395,7 @@ bool HitTestResult::mediaSupportsFullscreen() const
 {
 #if ENABLE(VIDEO)
     HTMLMediaElement* mediaElt(mediaElement());
-    return (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag) && mediaElt->supportsFullscreen());
+    return is<HTMLVideoElement>(mediaElt) && mediaElt->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard);
 #else
     return false;
 #endif
@@ -512,15 +404,15 @@ bool HitTestResult::mediaSupportsFullscreen() const
 #if ENABLE(VIDEO)
 HTMLMediaElement* HitTestResult::mediaElement() const
 {
-    if (!(m_innerNonSharedNode && m_innerNonSharedNode->document()))
-        return 0;
+    if (!m_innerNonSharedNode)
+        return nullptr;
 
     if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia()))
-        return 0;
+        return nullptr;
 
-    if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag))
-        return static_cast<HTMLMediaElement*>(m_innerNonSharedNode.get());
-    return 0;
+    if (is<HTMLMediaElement>(*m_innerNonSharedNode))
+        return downcast<HTMLMediaElement>(m_innerNonSharedNode.get());
+    return nullptr;
 }
 #endif
 
@@ -540,14 +432,37 @@ void HitTestResult::toggleMediaLoopPlayback() const
 #endif
 }
 
+bool HitTestResult::mediaIsInFullscreen() const
+{
+#if ENABLE(VIDEO)
+    if (HTMLMediaElement* mediaElement = this->mediaElement())
+        return mediaElement->isVideo() && mediaElement->isStandardFullscreen();
+#endif
+    return false;
+}
+
+void HitTestResult::toggleMediaFullscreenState() const
+{
+#if ENABLE(VIDEO)
+    if (HTMLMediaElement* mediaElement = this->mediaElement()) {
+        if (mediaElement->isVideo() && mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
+            UserGestureIndicator indicator(ProcessingUserGesture, &mediaElement->document());
+            mediaElement->toggleStandardFullscreenState();
+        }
+    }
+#endif
+}
+
 void HitTestResult::enterFullscreenForVideo() const
 {
 #if ENABLE(VIDEO)
-    HTMLMediaElement* mediaElt(mediaElement());
-    if (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag)) {
-        HTMLVideoElement* videoElt = static_cast<HTMLVideoElement*>(mediaElt);
-        if (!videoElt->isFullscreen() && mediaElt->supportsFullscreen())
-            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
 }
@@ -555,8 +470,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;
 }
@@ -600,7 +515,7 @@ bool HitTestResult::mediaIsVideo() const
 {
 #if ENABLE(VIDEO)
     if (HTMLMediaElement* mediaElt = mediaElement())
-        return mediaElt->hasTagName(HTMLNames::videoTag);
+        return is<HTMLVideoElement>(*mediaElt);
 #endif
     return false;
 }
@@ -622,37 +537,54 @@ void HitTestResult::toggleMediaMuteState() const
 #endif
 }
 
-KURL HitTestResult::absoluteLinkURL() const
+bool HitTestResult::isDownloadableMedia() const
 {
-    if (!(m_innerURLElement && m_innerURLElement->document()))
-        return KURL();
-
-    AtomicString urlString;
-    if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || 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 KURL();
 
-    return m_innerURLElement->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString));
+    return false;
 }
 
-bool HitTestResult::isLiveLink() const
+bool HitTestResult::isOverTextInsideFormControlElement() const
 {
-    if (!(m_innerURLElement && m_innerURLElement->document()))
+    Node* node = innerNode();
+    if (!node)
         return false;
 
-    if (m_innerURLElement->hasTagName(aTag))
-        return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink();
-#if ENABLE(SVG)
-    if (m_innerURLElement->hasTagName(SVGNames::aTag))
-        return m_innerURLElement->isLink();
-#endif
+    if (!is<Element>(*node) || !downcast<Element>(*node).isTextField())
+        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();
+}
+
+URL HitTestResult::absoluteLinkURL() const
+{
+    if (m_innerURLElement)
+        return m_innerURLElement->absoluteLinkURL();
+    return URL();
+}
+
+bool HitTestResult::isOverLink() const
+{
+    return m_innerURLElement && m_innerURLElement->isLink();
 }
 
 String HitTestResult::titleDisplayString() const
@@ -679,58 +611,64 @@ bool HitTestResult::isContentEditable() const
     if (!m_innerNonSharedNode)
         return false;
 
-    if (m_innerNonSharedNode->hasTagName(textareaTag))
+    if (is<HTMLTextAreaElement>(*m_innerNonSharedNode))
         return true;
 
-    if (m_innerNonSharedNode->hasTagName(inputTag))
-        return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField();
+    if (is<HTMLInputElement>(*m_innerNonSharedNode))
+        return downcast<HTMLInputElement>(*m_innerNonSharedNode).isTextField();
 
-    return m_innerNonSharedNode->rendererIsEditable();
+    return m_innerNonSharedNode->hasEditableStyle();
 }
 
-bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
+HitTestProgress HitTestResult::addNodeToListBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
 {
-    // If it is not a rect-based hit test, this method has to be no-op.
-    // Return false, so the hit test stops.
-    if (!isRectBasedTest())
-        return false;
+    // If it is not a list-based hit test, this method has to be no-op.
+    if (!request.resultIsElementList()) {
+        ASSERT(!isRectBasedTest());
+        return HitTestProgress::Stop;
+    }
 
-    // If node is null, return true so the hit test can continue.
     if (!node)
-        return true;
+        return HitTestProgress::Continue;
 
-    if (!request.allowsShadowContent())
-        node = node->deprecatedShadowAncestorNode();
+    if (request.disallowsUserAgentShadowContent() && node->isInUserAgentShadowTree())
+        node = node->document().ancestorNodeInThisScope(node);
 
-    mutableRectBasedTestResult().add(node);
+    mutableListBasedTestResult().add(node);
+
+    if (request.includesAllElementsUnderPoint())
+        return HitTestProgress::Continue;
 
     bool regionFilled = rect.contains(locationInContainer.boundingBox());
-    return !regionFilled;
+    return regionFilled ? HitTestProgress::Stop : HitTestProgress::Continue;
 }
 
-bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
+HitTestProgress HitTestResult::addNodeToListBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
 {
-    // If it is not a rect-based hit test, this method has to be no-op.
-    // Return false, so the hit test stops.
-    if (!isRectBasedTest())
-        return false;
+    // If it is not a list-based hit test, this method has to be no-op.
+    if (!request.resultIsElementList()) {
+        ASSERT(!isRectBasedTest());
+        return HitTestProgress::Stop;
+    }
 
-    // If node is null, return true so the hit test can continue.
     if (!node)
-        return true;
+        return HitTestProgress::Continue;
+
+    if (request.disallowsUserAgentShadowContent() && node->isInUserAgentShadowTree())
+        node = node->document().ancestorNodeInThisScope(node);
 
-    if (!request.allowsShadowContent())
-        node = node->deprecatedShadowAncestorNode();
+    mutableListBasedTestResult().add(node);
 
-    mutableRectBasedTestResult().add(node);
+    if (request.includesAllElementsUnderPoint())
+        return HitTestProgress::Continue;
 
     bool regionFilled = rect.contains(locationInContainer.boundingBox());
-    return !regionFilled;
+    return regionFilled ? HitTestProgress::Stop : HitTestProgress::Continue;
 }
 
-void HitTestResult::append(const HitTestResult& other)
+void HitTestResult::append(const HitTestResult& other, const HitTestRequest& request)
 {
-    ASSERT(isRectBasedTest() && other.isRectBasedTest());
+    ASSERT_UNUSED(request, request.resultIsElementList());
 
     if (!m_innerNode && other.innerNode()) {
         m_innerNode = other.innerNode();
@@ -742,25 +680,25 @@ void HitTestResult::append(const HitTestResult& other)
         m_isOverWidget = other.isOverWidget();
     }
 
-    if (other.m_rectBasedTestResult) {
-        NodeSet& set = mutableRectBasedTestResult();
-        for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
-            set.add(it->get());
+    if (other.m_listBasedTestResult) {
+        NodeSet& set = mutableListBasedTestResult();
+        for (auto node : *other.m_listBasedTestResult)
+            set.add(node.get());
     }
 }
 
-const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
+const HitTestResult::NodeSet& HitTestResult::listBasedTestResult() const
 {
-    if (!m_rectBasedTestResult)
-        m_rectBasedTestResult = adoptPtr(new NodeSet);
-    return *m_rectBasedTestResult;
+    if (!m_listBasedTestResult)
+        m_listBasedTestResult = std::make_unique<NodeSet>();
+    return *m_listBasedTestResult;
 }
 
-HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
+HitTestResult::NodeSet& HitTestResult::mutableListBasedTestResult()
 {
-    if (!m_rectBasedTestResult)
-        m_rectBasedTestResult = adoptPtr(new NodeSet);
-    return *m_rectBasedTestResult;
+    if (!m_listBasedTestResult)
+        m_listBasedTestResult = std::make_unique<NodeSet>();
+    return *m_listBasedTestResult;
 }
 
 Vector<String> HitTestResult::dictationAlternatives() const
@@ -769,15 +707,15 @@ Vector<String> HitTestResult::dictationAlternatives() const
     if (!m_innerNonSharedNode)
         return Vector<String>();
 
-    DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(pointInInnerNodeFrame(), DocumentMarker::DictationAlternatives);
+    DocumentMarker* marker = m_innerNonSharedNode->document().markers().markerContainingPoint(pointInInnerNodeFrame(), DocumentMarker::DictationAlternatives);
     if (!marker)
         return Vector<String>();
 
-    Frame* frame = innerNonSharedNode()->document()->frame();
+    Frame* frame = innerNonSharedNode()->document().frame();
     if (!frame)
         return Vector<String>();
 
-    return frame->editor()->dictationAlternativesForMarker(marker);
+    return frame->editor().dictationAlternativesForMarker(*marker);
 }
 
 Node* HitTestResult::targetNode() const
@@ -785,24 +723,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
 {
-    for (Node* node = m_innerNode.get(); node; node = node->parentNode()) {
-        if (node->isElementNode())
-            return toElement(node);
+    for (Node* node = m_innerNode.get(); node; node = node->parentInComposedTree()) {
+        if (is<Element>(*node))
+            return downcast<Element>(node);
     }
+    return nullptr;
+}
 
-    return 0;
+Element* HitTestResult::innerNonSharedElement() const
+{
+    Node* node = m_innerNonSharedNode.get();
+    if (!node)
+        return nullptr;
+    if (is<Element>(*node))
+        return downcast<Element>(node);
+    return node->parentElement();
+}
+
+String HitTestResult::linkSuggestedFilename() const
+{
+    auto* urlElement = URLElement();
+    if (!is<HTMLAnchorElement>(urlElement))
+        return nullAtom();
+    return ResourceResponse::sanitizeSuggestedFilename(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