diff options
Diffstat (limited to 'Source/WebCore/rendering/HitTestResult.cpp')
-rw-r--r-- | Source/WebCore/rendering/HitTestResult.cpp | 378 |
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 |