Diff
Modified: trunk/Source/WebCore/ChangeLog (285654 => 285655)
--- trunk/Source/WebCore/ChangeLog 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/ChangeLog 2021-11-11 21:52:01 UTC (rev 285655)
@@ -1,3 +1,87 @@
+2021-11-11 Wenson Hsieh <[email protected]>
+
+ Move image overlay code out of HTMLElement and into a separate helper file
+ https://bugs.webkit.org/show_bug.cgi?id=232974
+
+ Reviewed by Antti Koivisto.
+
+ Move code for updating, querying, and removing image overlays out of HTMLElement.cpp and HTMLElement.h and into
+ separate helper files instead (ImageOverlay.h and ImageOverlay.cpp). Future patches in this area will extend the
+ functionality of these overlays, so this will help avoid code bloat inside HTMLElement when doing so.
+
+ No change in behavior.
+
+ * Headers.cmake:
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * dom/ImageOverlay.cpp: Added.
+ (WebCore::ImageOverlay::imageOverlayElementIdentifier):
+ (WebCore::ImageOverlay::imageOverlayDataDetectorClassName):
+ (WebCore::ImageOverlay::hasOverlay):
+ (WebCore::ImageOverlay::imageOverlayHost):
+ (WebCore::ImageOverlay::isDataDetectorResult):
+ (WebCore::ImageOverlay::isInsideOverlay):
+ (WebCore::ImageOverlay::isOverlayText):
+ (WebCore::ImageOverlay::removeOverlaySoonIfNeeded):
+ (WebCore::ImageOverlay::containerRect):
+ (WebCore::ImageOverlay::updateWithTextRecognitionResult):
+ * dom/ImageOverlay.h: Added.
+
+ Additionally wrap the new image overlay helper functions in the `ImageOverlay` namespace. This also allows us to
+ shorten some of the function names, since they're already under the namespace, so duplicating "ImageOverlay" in
+ the function name itself is unnecessary.
+
+ * editing/Editor.cpp:
+ (WebCore::Editor::performCutOrCopy):
+ (WebCore::scanForTelephoneNumbers):
+ * editing/FrameSelection.cpp:
+ (WebCore::FrameSelection::contains const):
+ * editing/TextIterator.cpp:
+ (WebCore::TextIterator::handleReplacedElement):
+ * html/HTMLElement.cpp:
+ (WebCore::HTMLElement::shouldExtendSelectionToTargetNode):
+ (WebCore::HTMLElement::selectionRenderingBehavior):
+ (WebCore::imageOverlayElementIdentifier): Deleted.
+ (WebCore::imageOverlayDataDetectorClassName): Deleted.
+ (WebCore::HTMLElement::hasImageOverlay const): Deleted.
+ (WebCore::imageOverlayHost): Deleted.
+ (WebCore::HTMLElement::isImageOverlayDataDetectorResult const): Deleted.
+ (WebCore::HTMLElement::isInsideImageOverlay): Deleted.
+ (WebCore::HTMLElement::isImageOverlayText): Deleted.
+ (WebCore::HTMLElement::removeImageOverlaySoonIfNeeded): Deleted.
+ (WebCore::HTMLElement::containerRectForTextRecognition): Deleted.
+ (WebCore::HTMLElement::updateWithTextRecognitionResult): Deleted.
+ * html/HTMLElement.h:
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::seekWithTolerance):
+ (WebCore::HTMLMediaElement::playInternal):
+ * page/ContextMenuController.cpp:
+ (WebCore::ContextMenuController::populate):
+ * page/DragController.cpp:
+ (WebCore::DragController::draggableElement const):
+ (WebCore::DragController::startDrag):
+ * page/EventHandler.cpp:
+ (WebCore::nodeToSelectOnMouseDownForNode):
+ (WebCore::EventHandler::canMouseDownStartSelect):
+ (WebCore::EventHandler::handleMousePressEvent):
+ (WebCore::EventHandler::updateSelectionForMouseDrag):
+ (WebCore::EventHandler::hitTestResultAtPoint const):
+ (WebCore::EventHandler::selectCursor):
+ * page/ImageOverlayController.cpp:
+ (WebCore::ImageOverlayController::selectionQuadsDidChange):
+ * page/Page.cpp:
+ (WebCore::Page::updateElementsWithTextRecognitionResults):
+ * page/mac/ImageOverlayControllerMac.mm:
+ (WebCore::ImageOverlayController::updateDataDetectorHighlights):
+ (WebCore::ImageOverlayController::elementUnderMouseDidChange):
+ * rendering/HitTestResult.cpp:
+ (WebCore::HitTestResult::nodeForImageData const):
+ (WebCore::HitTestResult::addNodeToListBasedTestResultCommon):
+ * rendering/RenderImage.cpp:
+ (WebCore::RenderImage::RenderImage):
+ * testing/Internals.cpp:
+ (WebCore::Internals::installImageOverlay):
+
2021-11-11 Adrian Perez de Castro <[email protected]>
Non-unified build fixes, early November 2021 edition, bis
Modified: trunk/Source/WebCore/Headers.cmake (285654 => 285655)
--- trunk/Source/WebCore/Headers.cmake 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/Headers.cmake 2021-11-11 21:52:01 UTC (rev 285655)
@@ -513,6 +513,7 @@
dom/FragmentScriptingPermission.h
dom/FullscreenManager.h
dom/GCReachableRef.h
+ dom/ImageOverlay.h
dom/InlineStyleSheetOwner.h
dom/KeyboardEvent.h
dom/LiveNodeList.h
Modified: trunk/Source/WebCore/Sources.txt (285654 => 285655)
--- trunk/Source/WebCore/Sources.txt 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/Sources.txt 2021-11-11 21:52:01 UTC (rev 285655)
@@ -964,6 +964,7 @@
dom/IdTargetObserverRegistry.cpp
dom/IdleCallbackController.cpp
dom/IdleDeadline.cpp
+dom/ImageOverlay.cpp
dom/InlineClassicScript.cpp
dom/InlineStyleSheetOwner.cpp
dom/InputEvent.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (285654 => 285655)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2021-11-11 21:52:01 UTC (rev 285655)
@@ -5444,6 +5444,7 @@
F45775CE241437D5002DF1A0 /* InlinePathData.h in Headers */ = {isa = PBXBuildFile; fileRef = F45775CD241437D5002DF1A0 /* InlinePathData.h */; settings = {ATTRIBUTES = (Private, ); }; };
F46729281E0DE68500ACC3D8 /* ScrollSnapOffsetsInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F46729251E0DE5AB00ACC3D8 /* ScrollSnapOffsetsInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
F46C447E234654540039A79D /* ClipboardItemBindingsDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = F46C447C234654540039A79D /* ClipboardItemBindingsDataSource.h */; };
+ F46D5386273D7E460009FA80 /* ImageOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = F46D5385273D7E3F0009FA80 /* ImageOverlay.h */; settings = {ATTRIBUTES = (Private, ); }; };
F473845825DDE9FB006DE8DD /* DataOwnerType.h in Headers */ = {isa = PBXBuildFile; fileRef = F473845725DDE9FB006DE8DD /* DataOwnerType.h */; settings = {ATTRIBUTES = (Private, ); }; };
F47A09D120A93A9700240FAE /* DisabledAdaptations.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A09CF20A939F600240FAE /* DisabledAdaptations.h */; settings = {ATTRIBUTES = (Private, ); }; };
F47A5E3E195B8C8A00483100 /* StyleScrollSnapPoints.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A5E3B195B8C8A00483100 /* StyleScrollSnapPoints.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -17244,6 +17245,8 @@
F46C447D234654540039A79D /* ClipboardItemBindingsDataSource.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ClipboardItemBindingsDataSource.cpp; sourceTree = "<group>"; };
F46C44802346547A0039A79D /* ClipboardItemPasteboardDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClipboardItemPasteboardDataSource.h; sourceTree = "<group>"; };
F46C44812346547A0039A79D /* ClipboardItemPasteboardDataSource.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ClipboardItemPasteboardDataSource.cpp; sourceTree = "<group>"; };
+ F46D5384273D7E3E0009FA80 /* ImageOverlay.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ImageOverlay.cpp; sourceTree = "<group>"; };
+ F46D5385273D7E3F0009FA80 /* ImageOverlay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageOverlay.h; sourceTree = "<group>"; };
F473845725DDE9FB006DE8DD /* DataOwnerType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataOwnerType.h; sourceTree = "<group>"; };
F47A09CF20A939F600240FAE /* DisabledAdaptations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DisabledAdaptations.h; sourceTree = "<group>"; };
F47A09D420A9DD0400240FAE /* DisabledAdaptations.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DisabledAdaptations.cpp; sourceTree = "<group>"; };
@@ -31407,6 +31410,8 @@
C3CF17A315B0063F00276D39 /* IdTargetObserverRegistry.h */,
8AB4BC76126FDB7100DEB727 /* IgnoreDestructiveWriteCountIncrementer.h */,
467302011C4EFE6600BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h */,
+ F46D5384273D7E3E0009FA80 /* ImageOverlay.cpp */,
+ F46D5385273D7E3F0009FA80 /* ImageOverlay.h */,
E30592611E27A38C00D57C98 /* InlineClassicScript.cpp */,
E30592621E27A38C00D57C98 /* InlineClassicScript.h */,
AA4C3A740B2B1679002334A2 /* InlineStyleSheetOwner.cpp */,
@@ -33832,6 +33837,7 @@
089582560E857A7E00F82C83 /* ImageLoader.h in Headers */,
BC7F44A80B9E324E00A9D081 /* ImageObserver.h in Headers */,
2D5A5931152525D00036EE51 /* ImageOrientation.h in Headers */,
+ F46D5386273D7E460009FA80 /* ImageOverlay.h in Headers */,
F482434B260C33060022497C /* ImageOverlayController.h in Headers */,
F446EDE1265DB1E50031DA8F /* ImageOverlayDataDetectionResultIdentifier.h in Headers */,
72283F0E230B268C00F5D828 /* ImagePaintingOptions.h in Headers */,
Added: trunk/Source/WebCore/dom/ImageOverlay.cpp (0 => 285655)
--- trunk/Source/WebCore/dom/ImageOverlay.cpp (rev 0)
+++ trunk/Source/WebCore/dom/ImageOverlay.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ImageOverlay.h"
+
+#include "DOMTokenList.h"
+#include "Document.h"
+#include "ElementChildIterator.h"
+#include "EventHandler.h"
+#include "EventLoop.h"
+#include "FloatSize.h"
+#include "GeometryUtilities.h"
+#include "HTMLBRElement.h"
+#include "HTMLDivElement.h"
+#include "HTMLMediaElement.h"
+#include "HTMLStyleElement.h"
+#include "MediaControlsHost.h"
+#include "Page.h"
+#include "Quirks.h"
+#include "RenderImage.h"
+#include "ShadowRoot.h"
+#include "SimpleRange.h"
+#include "Text.h"
+#include "TextRecognitionResult.h"
+#include "UserAgentStyleSheets.h"
+#include <wtf/Range.h>
+#include <wtf/WeakPtr.h>
+#include <wtf/text/AtomString.h>
+
+#if ENABLE(DATA_DETECTION)
+#include "DataDetection.h"
+#endif
+
+namespace WebCore {
+namespace ImageOverlay {
+
+static const AtomString& imageOverlayElementIdentifier()
+{
+ static MainThreadNeverDestroyed<const AtomString> identifier("image-overlay", AtomString::ConstructFromLiteral);
+ return identifier;
+}
+
+static const AtomString& imageOverlayDataDetectorClassName()
+{
+ static MainThreadNeverDestroyed<const AtomString> className("image-overlay-data-detector-result", AtomString::ConstructFromLiteral);
+ return className;
+}
+
+bool hasOverlay(const HTMLElement& element)
+{
+ auto shadowRoot = element.shadowRoot();
+ if (LIKELY(!shadowRoot || shadowRoot->mode() != ShadowRootMode::UserAgent))
+ return false;
+
+ return shadowRoot->hasElementWithId(*imageOverlayElementIdentifier().impl());
+}
+
+static RefPtr<HTMLElement> imageOverlayHost(const Node& node)
+{
+ auto host = node.shadowHost();
+ if (!is<HTMLElement>(host))
+ return nullptr;
+
+ RefPtr element { &downcast<HTMLElement>(*host) };
+ return hasOverlay(*element) ? element : nullptr;
+}
+
+bool isDataDetectorResult(const HTMLElement& element)
+{
+ return imageOverlayHost(element) && element.hasClass() && element.classNames().contains(imageOverlayDataDetectorClassName());
+}
+
+bool isInsideOverlay(const SimpleRange& range)
+{
+ RefPtr commonAncestor = commonInclusiveAncestor<ComposedTree>(range);
+ if (!commonAncestor)
+ return false;
+
+ return isInsideOverlay(*commonAncestor);
+}
+
+bool isInsideOverlay(const Node& node)
+{
+ auto host = imageOverlayHost(node);
+ if (!host)
+ return false;
+
+ return host->userAgentShadowRoot()->contains(node);
+}
+
+bool isOverlayText(const Node* node)
+{
+ return node && isOverlayText(*node);
+}
+
+bool isOverlayText(const Node& node)
+{
+ auto host = imageOverlayHost(node);
+ if (!host)
+ return false;
+
+ if (RefPtr overlay = static_cast<TreeScope&>(*host->userAgentShadowRoot()).getElementById(imageOverlayElementIdentifier()))
+ return node.isDescendantOf(*overlay);
+
+ return false;
+}
+
+void removeOverlaySoonIfNeeded(HTMLElement& element)
+{
+ if (!hasOverlay(element))
+ return;
+
+ element.document().eventLoop().queueTask(TaskSource::InternalAsyncTask, [weakElement = WeakPtr { element }] {
+ RefPtr protectedElement = weakElement.get();
+ if (!protectedElement)
+ return;
+
+ RefPtr shadowRoot = protectedElement->userAgentShadowRoot();
+ if (!shadowRoot)
+ return;
+
+ if (RefPtr overlay = static_cast<TreeScope&>(*shadowRoot).getElementById(imageOverlayElementIdentifier()))
+ overlay->remove();
+
+#if ENABLE(IMAGE_ANALYSIS)
+ if (auto page = protectedElement->document().page())
+ page->resetTextRecognitionResult(*protectedElement);
+#endif
+ });
+}
+
+#if ENABLE(IMAGE_ANALYSIS)
+
+IntRect containerRect(HTMLElement& element)
+{
+ auto* renderer = element.renderer();
+ if (!is<RenderImage>(renderer))
+ return { };
+
+ if (!renderer->opacity())
+ return { 0, 0, element.offsetWidth(), element.offsetHeight() };
+
+ return enclosingIntRect(downcast<RenderImage>(*renderer).replacedContentRect());
+}
+
+void updateWithTextRecognitionResult(HTMLElement& element, const TextRecognitionResult& result, CacheTextRecognitionResults cacheTextRecognitionResults)
+{
+ static MainThreadNeverDestroyed<const AtomString> imageOverlayLineClass("image-overlay-line", AtomString::ConstructFromLiteral);
+ static MainThreadNeverDestroyed<const AtomString> imageOverlayTextClass("image-overlay-text", AtomString::ConstructFromLiteral);
+
+ struct TextRecognitionLineElements {
+ Ref<HTMLDivElement> line;
+ Vector<Ref<HTMLElement>> children;
+ };
+
+ struct TextRecognitionElements {
+ RefPtr<HTMLDivElement> root;
+ Vector<TextRecognitionLineElements> lines;
+ Vector<Ref<HTMLDivElement>> dataDetectors;
+ };
+
+ bool hadExistingTextRecognitionElements = false;
+ TextRecognitionElements textRecognitionElements;
+ RefPtr<HTMLElement> mediaControlsContainer;
+ if (RefPtr shadowRoot = element.shadowRoot()) {
+#if ENABLE(MODERN_MEDIA_CONTROLS)
+ if (is<HTMLMediaElement>(element)) {
+ if (RefPtr controlsHost = downcast<HTMLMediaElement>(element).mediaControlsHost()) {
+ auto& containerClass = controlsHost->mediaControlsContainerClassName();
+ for (auto& child : childrenOfType<HTMLDivElement>(*shadowRoot)) {
+ if (child.hasClass() && child.classNames().contains(containerClass)) {
+ mediaControlsContainer = &child;
+ break;
+ }
+ }
+ }
+ }
+#endif
+ if (hasOverlay(element)) {
+ RefPtr<ContainerNode> containerForImageOverlay;
+ if (mediaControlsContainer)
+ containerForImageOverlay = mediaControlsContainer;
+ else
+ containerForImageOverlay = shadowRoot;
+ for (auto& child : childrenOfType<HTMLDivElement>(*containerForImageOverlay)) {
+ if (child.getIdAttribute() == imageOverlayElementIdentifier()) {
+ textRecognitionElements.root = &child;
+ hadExistingTextRecognitionElements = true;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (textRecognitionElements.root) {
+ for (auto& lineOrDataDetector : childrenOfType<HTMLDivElement>(*textRecognitionElements.root)) {
+ if (!lineOrDataDetector.hasClass())
+ continue;
+
+ if (lineOrDataDetector.classList().contains(imageOverlayLineClass)) {
+ TextRecognitionLineElements lineElements { lineOrDataDetector, { } };
+ for (auto& text : childrenOfType<HTMLDivElement>(lineOrDataDetector))
+ lineElements.children.append(text);
+ textRecognitionElements.lines.append(WTFMove(lineElements));
+ } else if (lineOrDataDetector.classList().contains(imageOverlayDataDetectorClassName()))
+ textRecognitionElements.dataDetectors.append(lineOrDataDetector);
+ }
+
+ bool canUseExistingTextRecognitionElements = ([&] {
+ if (result.dataDetectors.size() != textRecognitionElements.dataDetectors.size())
+ return false;
+
+ if (result.lines.size() != textRecognitionElements.lines.size())
+ return false;
+
+ for (size_t lineIndex = 0; lineIndex < result.lines.size(); ++lineIndex) {
+ auto& childResults = result.lines[lineIndex].children;
+ auto& childTextElements = textRecognitionElements.lines[lineIndex].children;
+ if (childResults.size() != childTextElements.size())
+ return false;
+
+ for (size_t childIndex = 0; childIndex < childResults.size(); ++childIndex) {
+ if (childResults[childIndex].text != childTextElements[childIndex]->textContent().stripWhiteSpace())
+ return false;
+ }
+ }
+
+ return true;
+ })();
+
+ if (!canUseExistingTextRecognitionElements) {
+ textRecognitionElements.root->remove();
+ textRecognitionElements = { };
+ }
+ }
+
+ if (result.isEmpty())
+ return;
+
+ Ref document = element.document();
+ Ref shadowRoot = element.ensureUserAgentShadowRoot();
+ if (!textRecognitionElements.root) {
+ auto rootContainer = HTMLDivElement::create(document.get());
+ rootContainer->setIdAttribute(imageOverlayElementIdentifier());
+ if (document->isImageDocument())
+ rootContainer->setInlineStyleProperty(CSSPropertyWebkitUserSelect, CSSValueText);
+
+ if (mediaControlsContainer)
+ mediaControlsContainer->appendChild(rootContainer);
+ else
+ shadowRoot->appendChild(rootContainer);
+ textRecognitionElements.root = rootContainer.copyRef();
+ textRecognitionElements.lines.reserveInitialCapacity(result.lines.size());
+ for (auto& line : result.lines) {
+ auto lineContainer = HTMLDivElement::create(document.get());
+ lineContainer->classList().add(imageOverlayLineClass);
+ rootContainer->appendChild(lineContainer);
+ TextRecognitionLineElements lineElements { lineContainer, { } };
+ lineElements.children.reserveInitialCapacity(line.children.size());
+ for (size_t childIndex = 0; childIndex < line.children.size(); ++childIndex) {
+ auto& child = line.children[childIndex];
+ auto textContainer = HTMLDivElement::create(document.get());
+ textContainer->classList().add(imageOverlayTextClass);
+ lineContainer->appendChild(textContainer);
+ textContainer->appendChild(Text::create(document.get(), child.hasLeadingWhitespace ? makeString('\n', child.text) : child.text));
+ lineElements.children.uncheckedAppend(WTFMove(textContainer));
+ }
+
+ lineContainer->appendChild(HTMLBRElement::create(document.get()));
+ textRecognitionElements.lines.uncheckedAppend(WTFMove(lineElements));
+ }
+
+#if ENABLE(DATA_DETECTION)
+ textRecognitionElements.dataDetectors.reserveInitialCapacity(result.dataDetectors.size());
+ for (auto& dataDetector : result.dataDetectors) {
+ auto dataDetectorContainer = DataDetection::createElementForImageOverlay(document.get(), dataDetector);
+ dataDetectorContainer->classList().add(imageOverlayDataDetectorClassName());
+ rootContainer->appendChild(dataDetectorContainer);
+ textRecognitionElements.dataDetectors.uncheckedAppend(WTFMove(dataDetectorContainer));
+ }
+#endif // ENABLE(DATA_DETECTION)
+
+ if (document->quirks().needsToForceUserSelectWhenInstallingImageOverlay())
+ element.setInlineStyleProperty(CSSPropertyWebkitUserSelect, CSSValueText);
+ }
+
+ if (!hadExistingTextRecognitionElements) {
+ static MainThreadNeverDestroyed<const String> shadowStyle(StringImpl::createWithoutCopying(imageOverlayUserAgentStyleSheet, sizeof(imageOverlayUserAgentStyleSheet)));
+ auto style = HTMLStyleElement::create(HTMLNames::styleTag, document.get(), false);
+ style->setTextContent(shadowStyle);
+ shadowRoot->appendChild(WTFMove(style));
+ }
+
+ document->updateLayoutIgnorePendingStylesheets();
+
+ auto* renderer = element.renderer();
+ if (!is<RenderImage>(renderer))
+ return;
+
+ downcast<RenderImage>(*renderer).setHasImageOverlay();
+
+ auto containerRect = ImageOverlay::containerRect(element);
+ auto convertToContainerCoordinates = [&](const FloatQuad& normalizedQuad) {
+ auto quad = normalizedQuad;
+ quad.scale(containerRect.width(), containerRect.height());
+ quad.move(containerRect.x(), containerRect.y());
+ return quad;
+ };
+
+ bool applyUserSelectAll = document->isImageDocument() || renderer->style().userSelect() != UserSelect::None;
+ for (size_t lineIndex = 0; lineIndex < result.lines.size(); ++lineIndex) {
+ auto& lineElements = textRecognitionElements.lines[lineIndex];
+ auto& lineContainer = lineElements.line;
+ auto& line = result.lines[lineIndex];
+ auto lineQuad = convertToContainerCoordinates(line.normalizedQuad);
+ if (lineQuad.isEmpty())
+ continue;
+
+ auto lineBounds = rotatedBoundingRectWithMinimumAngleOfRotation(lineQuad, 0.01);
+ lineContainer->setInlineStyleProperty(CSSPropertyWidth, lineBounds.size.width(), CSSUnitType::CSS_PX);
+ lineContainer->setInlineStyleProperty(CSSPropertyHeight, lineBounds.size.height(), CSSUnitType::CSS_PX);
+ lineContainer->setInlineStyleProperty(CSSPropertyTransform, makeString(
+ "translate("_s,
+ std::round(lineBounds.center.x() - (lineBounds.size.width() / 2)), "px, "_s,
+ std::round(lineBounds.center.y() - (lineBounds.size.height() / 2)), "px) "_s,
+ lineBounds.angleInRadians ? makeString("rotate("_s, lineBounds.angleInRadians, "rad) "_s) : emptyString()
+ ));
+
+ auto offsetAlongHorizontalAxis = [&](const FloatPoint& quadPoint1, const FloatPoint& quadPoint2) {
+ auto intervalLength = lineBounds.size.width();
+ auto mid = midPoint(quadPoint1, quadPoint2);
+ mid.moveBy(-lineBounds.center);
+ mid.rotate(-lineBounds.angleInRadians);
+ return intervalLength * clampTo<float>(0.5 + mid.x() / intervalLength, 0, 1);
+ };
+
+ auto offsetsAlongHorizontalAxis = line.children.map([&](auto& child) -> WTF::Range<float> {
+ auto textQuad = convertToContainerCoordinates(child.normalizedQuad);
+ return {
+ offsetAlongHorizontalAxis(textQuad.p1(), textQuad.p4()),
+ offsetAlongHorizontalAxis(textQuad.p2(), textQuad.p3())
+ };
+ });
+
+ for (size_t childIndex = 0; childIndex < line.children.size(); ++childIndex) {
+ auto& textContainer = lineElements.children[childIndex];
+ bool lineHasOneChild = line.children.size() == 1;
+ float horizontalMarginToMinimizeSelectionGaps = lineHasOneChild ? 0 : 0.125;
+ float horizontalOffset = lineHasOneChild ? 0 : -horizontalMarginToMinimizeSelectionGaps;
+ float horizontalExtent = lineHasOneChild ? 0 : horizontalMarginToMinimizeSelectionGaps;
+
+ if (lineHasOneChild) {
+ horizontalOffset += offsetsAlongHorizontalAxis[childIndex].begin();
+ horizontalExtent += offsetsAlongHorizontalAxis[childIndex].end();
+ } else if (!childIndex) {
+ horizontalOffset += offsetsAlongHorizontalAxis[childIndex].begin();
+ horizontalExtent += (offsetsAlongHorizontalAxis[childIndex].end() + offsetsAlongHorizontalAxis[childIndex + 1].begin()) / 2;
+ } else if (childIndex == line.children.size() - 1) {
+ horizontalOffset += (offsetsAlongHorizontalAxis[childIndex - 1].end() + offsetsAlongHorizontalAxis[childIndex].begin()) / 2;
+ horizontalExtent += offsetsAlongHorizontalAxis[childIndex].end();
+ } else {
+ horizontalOffset += (offsetsAlongHorizontalAxis[childIndex - 1].end() + offsetsAlongHorizontalAxis[childIndex].begin()) / 2;
+ horizontalExtent += (offsetsAlongHorizontalAxis[childIndex].end() + offsetsAlongHorizontalAxis[childIndex + 1].begin()) / 2;
+ }
+
+ FloatSize targetSize { horizontalExtent - horizontalOffset, lineBounds.size.height() };
+ if (targetSize.isEmpty()) {
+ textContainer->setInlineStyleProperty(CSSPropertyTransform, "scale(0, 0)");
+ continue;
+ }
+
+ document->updateLayoutIfDimensionsOutOfDate(textContainer);
+
+ FloatSize sizeBeforeTransform;
+ if (auto* renderer = textContainer->renderBoxModelObject()) {
+ sizeBeforeTransform = {
+ adjustLayoutUnitForAbsoluteZoom(renderer->offsetWidth(), *renderer).toFloat(),
+ adjustLayoutUnitForAbsoluteZoom(renderer->offsetHeight(), *renderer).toFloat(),
+ };
+ }
+
+ if (sizeBeforeTransform.isEmpty()) {
+ textContainer->setInlineStyleProperty(CSSPropertyTransform, "scale(0, 0)");
+ continue;
+ }
+
+ textContainer->setInlineStyleProperty(CSSPropertyTransform, makeString(
+ "translate("_s,
+ horizontalOffset + (targetSize.width() - sizeBeforeTransform.width()) / 2, "px, "_s,
+ (targetSize.height() - sizeBeforeTransform.height()) / 2, "px) "_s,
+ "scale("_s, targetSize.width() / sizeBeforeTransform.width(), ", "_s, targetSize.height() / sizeBeforeTransform.height(), ") "_s
+ ));
+
+ textContainer->setInlineStyleProperty(CSSPropertyWebkitUserSelect, applyUserSelectAll ? CSSValueAll : CSSValueNone);
+ }
+
+ if (document->isImageDocument())
+ lineContainer->setInlineStyleProperty(CSSPropertyCursor, CSSValueText);
+ }
+
+#if ENABLE(DATA_DETECTION)
+ for (size_t index = 0; index < result.dataDetectors.size(); ++index) {
+ auto dataDetectorContainer = textRecognitionElements.dataDetectors[index];
+ auto& dataDetector = result.dataDetectors[index];
+ if (dataDetector.normalizedQuads.isEmpty())
+ continue;
+
+ // FIXME: We should come up with a way to coalesce the bounding quads into one or more rotated rects with the same angle of rotation.
+ auto targetQuad = convertToContainerCoordinates(dataDetector.normalizedQuads.first());
+ auto targetBounds = rotatedBoundingRectWithMinimumAngleOfRotation(targetQuad, 0.01);
+ dataDetectorContainer->setInlineStyleProperty(CSSPropertyWidth, targetBounds.size.width(), CSSUnitType::CSS_PX);
+ dataDetectorContainer->setInlineStyleProperty(CSSPropertyHeight, targetBounds.size.height(), CSSUnitType::CSS_PX);
+ dataDetectorContainer->setInlineStyleProperty(CSSPropertyTransform, makeString(
+ "translate("_s,
+ std::round(targetBounds.center.x() - (targetBounds.size.width() / 2)), "px, "_s,
+ std::round(targetBounds.center.y() - (targetBounds.size.height() / 2)), "px) "_s,
+ targetBounds.angleInRadians ? makeString("rotate("_s, targetBounds.angleInRadians, "rad) "_s) : emptyString()
+ ));
+ }
+#endif // ENABLE(DATA_DETECTION)
+
+ if (RefPtr frame = document->frame())
+ frame->eventHandler().scheduleCursorUpdate();
+
+ if (cacheTextRecognitionResults == CacheTextRecognitionResults::Yes) {
+ if (auto* page = document->page())
+ page->cacheTextRecognitionResult(element, containerRect, result);
+ }
+}
+
+#endif // ENABLE(IMAGE_ANALYSIS)
+
+} // namespace ImageOverlay
+} // namespace WebCore
Added: trunk/Source/WebCore/dom/ImageOverlay.h (0 => 285655)
--- trunk/Source/WebCore/dom/ImageOverlay.h (rev 0)
+++ trunk/Source/WebCore/dom/ImageOverlay.h 2021-11-11 21:52:01 UTC (rev 285655)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "IntRect.h"
+
+namespace WebCore {
+
+class HTMLElement;
+class Node;
+
+struct SimpleRange;
+struct TextRecognitionResult;
+
+namespace ImageOverlay {
+
+WEBCORE_EXPORT bool hasOverlay(const HTMLElement&);
+WEBCORE_EXPORT bool isDataDetectorResult(const HTMLElement&);
+WEBCORE_EXPORT bool isInsideOverlay(const SimpleRange&);
+WEBCORE_EXPORT bool isInsideOverlay(const Node&);
+WEBCORE_EXPORT bool isOverlayText(const Node&);
+WEBCORE_EXPORT bool isOverlayText(const Node*);
+void removeOverlaySoonIfNeeded(HTMLElement&);
+IntRect containerRect(HTMLElement&);
+
+#if ENABLE(IMAGE_ANALYSIS)
+enum class CacheTextRecognitionResults : bool { No, Yes };
+WEBCORE_EXPORT void updateWithTextRecognitionResult(HTMLElement&, const TextRecognitionResult&, CacheTextRecognitionResults = CacheTextRecognitionResults::Yes);
+#endif
+
+} // namespace ImageOverlay
+
+} // namespace WebCore
Modified: trunk/Source/WebCore/editing/Editor.cpp (285654 => 285655)
--- trunk/Source/WebCore/editing/Editor.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/editing/Editor.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -74,6 +74,7 @@
#include "HTMLSpanElement.h"
#include "HTMLUListElement.h"
#include "HitTestResult.h"
+#include "ImageOverlay.h"
#include "IndentOutdentCommand.h"
#include "InputEvent.h"
#include "InsertListCommand.h"
@@ -1426,7 +1427,7 @@
updateMarkersForWordsAffectedByEditing(true);
}
- if (enclosingTextFormControl(m_document.selection().selection().start()) || (selection && HTMLElement::isInsideImageOverlay(*selection)))
+ if (enclosingTextFormControl(m_document.selection().selection().start()) || (selection && ImageOverlay::isInsideOverlay(*selection)))
Pasteboard::createForCopyAndPaste(PagePasteboardContext::create(m_document.pageID()))->writePlainText(selectedTextForDataTransfer(), canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace);
else {
RefPtr<HTMLImageElement> imageElement;
@@ -3679,7 +3680,7 @@
if (Ref startNode = range.startContainer(); startNode->hasEditableStyle())
return { };
- if (HTMLElement::isInsideImageOverlay(range))
+ if (ImageOverlay::isInsideOverlay(range))
return { };
auto text = plainText(range);
Modified: trunk/Source/WebCore/editing/FrameSelection.cpp (285654 => 285655)
--- trunk/Source/WebCore/editing/FrameSelection.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/editing/FrameSelection.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -52,6 +52,7 @@
#include "HTMLSelectElement.h"
#include "HitTestRequest.h"
#include "HitTestResult.h"
+#include "ImageOverlay.h"
#include "InlineRunAndOffset.h"
#include "LegacyInlineTextBox.h"
#include "Logging.h"
@@ -1917,7 +1918,7 @@
if (!innerNode || !innerNode->renderer())
return false;
- if (HTMLElement::isInsideImageOverlay(*range) && HTMLElement::isInsideImageOverlay(*innerNode)) {
+ if (ImageOverlay::isInsideOverlay(*range) && ImageOverlay::isInsideOverlay(*innerNode)) {
for (auto quad : RenderObject::absoluteTextQuads(*range, { RenderObject::BoundingRectBehavior::UseSelectionHeight })) {
if (!quad.isEmpty() && quad.containsPoint(point))
return true;
Modified: trunk/Source/WebCore/editing/TextIterator.cpp (285654 => 285655)
--- trunk/Source/WebCore/editing/TextIterator.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/editing/TextIterator.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -45,6 +45,7 @@
#include "HTMLSlotElement.h"
#include "HTMLTextAreaElement.h"
#include "HTMLTextFormControlElement.h"
+#include "ImageOverlay.h"
#include "LegacyInlineTextBox.h"
#include "NodeTraversal.h"
#include "Range.h"
@@ -737,7 +738,7 @@
}
}
- if (m_behaviors.contains(TextIteratorBehavior::EntersImageOverlays) && is<HTMLElement>(m_node) && downcast<HTMLElement>(*m_node).hasImageOverlay()) {
+ if (m_behaviors.contains(TextIteratorBehavior::EntersImageOverlays) && is<HTMLElement>(m_node) && ImageOverlay::hasOverlay(downcast<HTMLElement>(*m_node))) {
if (RefPtr shadowRoot = m_node->shadowRoot()) {
m_node = shadowRoot.get();
pushFullyClippedState(m_fullyClippedStack, *m_node);
Modified: trunk/Source/WebCore/html/HTMLElement.cpp (285654 => 285655)
--- trunk/Source/WebCore/html/HTMLElement.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/html/HTMLElement.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -44,11 +44,9 @@
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameView.h"
-#include "GeometryUtilities.h"
#include "HTMLBDIElement.h"
#include "HTMLBRElement.h"
#include "HTMLButtonElement.h"
-#include "HTMLDivElement.h"
#include "HTMLDocument.h"
#include "HTMLElementFactory.h"
#include "HTMLFieldSetElement.h"
@@ -60,13 +58,12 @@
#include "HTMLOptionElement.h"
#include "HTMLParserIdioms.h"
#include "HTMLSelectElement.h"
-#include "HTMLStyleElement.h"
#include "HTMLTextAreaElement.h"
#include "HTMLTextFormControlElement.h"
+#include "ImageOverlay.h"
#include "MediaControlsHost.h"
#include "NodeTraversal.h"
#include "RenderElement.h"
-#include "RenderImage.h"
#include "ScriptController.h"
#include "ScriptDisallowedScope.h"
#include "ShadowRoot.h"
@@ -83,18 +80,10 @@
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
-#if ENABLE(IMAGE_ANALYSIS)
-#include "TextRecognitionResult.h"
-#endif
-
#if PLATFORM(IOS_FAMILY)
#include "SelectionGeometry.h"
#endif
-#if ENABLE(DATA_DETECTION)
-#include "DataDetection.h"
-#endif
-
namespace WebCore {
WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLElement);
@@ -1253,417 +1242,19 @@
setAttributeWithoutSynchronization(enterkeyhintAttr, value);
}
-// FIXME: We should move all of this image overlay-specific logic out into a separate helper file (possibly as standalone functions)
-// to better compartmentalize this code.
-
-static const AtomString& imageOverlayElementIdentifier()
-{
- static MainThreadNeverDestroyed<const AtomString> identifier("image-overlay", AtomString::ConstructFromLiteral);
- return identifier;
-}
-
-static const AtomString& imageOverlayDataDetectorClassName()
-{
- static MainThreadNeverDestroyed<const AtomString> className("image-overlay-data-detector-result", AtomString::ConstructFromLiteral);
- return className;
-}
-
bool HTMLElement::shouldExtendSelectionToTargetNode(const Node& targetNode, const VisibleSelection& selectionBeforeUpdate)
{
- if (auto range = selectionBeforeUpdate.range(); range && isInsideImageOverlay(*range))
- return isImageOverlayText(targetNode);
+ if (auto range = selectionBeforeUpdate.range(); range && ImageOverlay::isInsideOverlay(*range))
+ return ImageOverlay::isOverlayText(targetNode);
return true;
}
-bool HTMLElement::hasImageOverlay() const
-{
- auto shadowRoot = this->shadowRoot();
- if (LIKELY(!shadowRoot || shadowRoot->mode() != ShadowRootMode::UserAgent))
- return false;
-
- return shadowRoot->hasElementWithId(*imageOverlayElementIdentifier().impl());
-}
-
-static RefPtr<HTMLElement> imageOverlayHost(const Node& node)
-{
- auto host = node.shadowHost();
- if (!is<HTMLElement>(host))
- return nullptr;
-
- RefPtr element { &downcast<HTMLElement>(*host) };
- return element->hasImageOverlay() ? element : nullptr;
-}
-
-bool HTMLElement::isImageOverlayDataDetectorResult() const
-{
- return imageOverlayHost(*this) && hasClass() && classNames().contains(imageOverlayDataDetectorClassName());
-}
-
-bool HTMLElement::isInsideImageOverlay(const SimpleRange& range)
-{
- RefPtr commonAncestor = commonInclusiveAncestor<ComposedTree>(range);
- if (!commonAncestor)
- return false;
-
- return isInsideImageOverlay(*commonAncestor);
-}
-
-bool HTMLElement::isInsideImageOverlay(const Node& node)
-{
- auto host = imageOverlayHost(node);
- if (!host)
- return false;
-
- return host->userAgentShadowRoot()->contains(node);
-}
-
-bool HTMLElement::isImageOverlayText(const Node* node)
-{
- return node && isImageOverlayText(*node);
-}
-
-bool HTMLElement::isImageOverlayText(const Node& node)
-{
- auto host = imageOverlayHost(node);
- if (!host)
- return false;
-
- if (RefPtr overlay = static_cast<TreeScope&>(*host->userAgentShadowRoot()).getElementById(imageOverlayElementIdentifier()))
- return node.isDescendantOf(*overlay);
-
- return false;
-}
-
-void HTMLElement::removeImageOverlaySoonIfNeeded()
-{
- if (!hasImageOverlay())
- return;
-
- document().eventLoop().queueTask(TaskSource::InternalAsyncTask, [weakThis = WeakPtr { *this }] {
- RefPtr protectedThis = weakThis.get();
- if (!protectedThis)
- return;
-
- RefPtr shadowRoot = protectedThis->userAgentShadowRoot();
- if (!shadowRoot)
- return;
-
- if (RefPtr overlay = static_cast<TreeScope&>(*shadowRoot).getElementById(imageOverlayElementIdentifier()))
- overlay->remove();
-
-#if ENABLE(IMAGE_ANALYSIS)
- if (auto page = protectedThis->document().page())
- page->resetTextRecognitionResult(*protectedThis);
-#endif
- });
-}
-
-#if ENABLE(IMAGE_ANALYSIS)
-
-IntRect HTMLElement::containerRectForTextRecognition()
-{
- auto* renderer = this->renderer();
- if (!is<RenderImage>(renderer))
- return { };
-
- if (!renderer->opacity())
- return { 0, 0, offsetWidth(), offsetHeight() };
-
- return enclosingIntRect(downcast<RenderImage>(*renderer).replacedContentRect());
-}
-
-void HTMLElement::updateWithTextRecognitionResult(const TextRecognitionResult& result, CacheTextRecognitionResults cacheTextRecognitionResults)
-{
- static MainThreadNeverDestroyed<const AtomString> imageOverlayLineClass("image-overlay-line", AtomString::ConstructFromLiteral);
- static MainThreadNeverDestroyed<const AtomString> imageOverlayTextClass("image-overlay-text", AtomString::ConstructFromLiteral);
-
- struct TextRecognitionLineElements {
- Ref<HTMLDivElement> line;
- Vector<Ref<HTMLElement>> children;
- };
-
- struct TextRecognitionElements {
- RefPtr<HTMLDivElement> root;
- Vector<TextRecognitionLineElements> lines;
- Vector<Ref<HTMLDivElement>> dataDetectors;
- };
-
- bool hadExistingTextRecognitionElements = false;
- TextRecognitionElements textRecognitionElements;
- RefPtr<HTMLElement> mediaControlsContainer;
- if (RefPtr shadowRoot = this->shadowRoot()) {
-#if ENABLE(MODERN_MEDIA_CONTROLS)
- if (is<HTMLMediaElement>(*this)) {
- if (RefPtr controlsHost = downcast<HTMLMediaElement>(*this).mediaControlsHost()) {
- auto& containerClass = controlsHost->mediaControlsContainerClassName();
- for (auto& child : childrenOfType<HTMLDivElement>(*shadowRoot)) {
- if (child.hasClass() && child.classNames().contains(containerClass)) {
- mediaControlsContainer = &child;
- break;
- }
- }
- }
- }
-#endif
- if (hasImageOverlay()) {
- RefPtr<ContainerNode> containerForImageOverlay;
- if (mediaControlsContainer)
- containerForImageOverlay = mediaControlsContainer;
- else
- containerForImageOverlay = shadowRoot;
- for (auto& child : childrenOfType<HTMLDivElement>(*containerForImageOverlay)) {
- if (child.getIdAttribute() == imageOverlayElementIdentifier()) {
- textRecognitionElements.root = &child;
- hadExistingTextRecognitionElements = true;
- continue;
- }
- }
- }
- }
-
- if (textRecognitionElements.root) {
- for (auto& lineOrDataDetector : childrenOfType<HTMLDivElement>(*textRecognitionElements.root)) {
- if (!lineOrDataDetector.hasClass())
- continue;
-
- if (lineOrDataDetector.classList().contains(imageOverlayLineClass)) {
- TextRecognitionLineElements lineElements { lineOrDataDetector, { } };
- for (auto& text : childrenOfType<HTMLDivElement>(lineOrDataDetector))
- lineElements.children.append(text);
- textRecognitionElements.lines.append(WTFMove(lineElements));
- } else if (lineOrDataDetector.classList().contains(imageOverlayDataDetectorClassName()))
- textRecognitionElements.dataDetectors.append(lineOrDataDetector);
- }
-
- bool canUseExistingTextRecognitionElements = ([&] {
- if (result.dataDetectors.size() != textRecognitionElements.dataDetectors.size())
- return false;
-
- if (result.lines.size() != textRecognitionElements.lines.size())
- return false;
-
- for (size_t lineIndex = 0; lineIndex < result.lines.size(); ++lineIndex) {
- auto& childResults = result.lines[lineIndex].children;
- auto& childTextElements = textRecognitionElements.lines[lineIndex].children;
- if (childResults.size() != childTextElements.size())
- return false;
-
- for (size_t childIndex = 0; childIndex < childResults.size(); ++childIndex) {
- if (childResults[childIndex].text != childTextElements[childIndex]->textContent().stripWhiteSpace())
- return false;
- }
- }
-
- return true;
- })();
-
- if (!canUseExistingTextRecognitionElements) {
- textRecognitionElements.root->remove();
- textRecognitionElements = { };
- }
- }
-
- if (result.isEmpty())
- return;
-
- Ref shadowRoot = ensureUserAgentShadowRoot();
- if (!textRecognitionElements.root) {
- auto rootContainer = HTMLDivElement::create(document());
- rootContainer->setIdAttribute(imageOverlayElementIdentifier());
- if (document().isImageDocument())
- rootContainer->setInlineStyleProperty(CSSPropertyWebkitUserSelect, CSSValueText);
-
- if (mediaControlsContainer)
- mediaControlsContainer->appendChild(rootContainer);
- else
- shadowRoot->appendChild(rootContainer);
- textRecognitionElements.root = rootContainer.copyRef();
- textRecognitionElements.lines.reserveInitialCapacity(result.lines.size());
- for (auto& line : result.lines) {
- auto lineContainer = HTMLDivElement::create(document());
- lineContainer->classList().add(imageOverlayLineClass);
- rootContainer->appendChild(lineContainer);
- TextRecognitionLineElements lineElements { lineContainer, { } };
- lineElements.children.reserveInitialCapacity(line.children.size());
- for (size_t childIndex = 0; childIndex < line.children.size(); ++childIndex) {
- auto& child = line.children[childIndex];
- auto textContainer = HTMLDivElement::create(document());
- textContainer->classList().add(imageOverlayTextClass);
- lineContainer->appendChild(textContainer);
- textContainer->appendChild(Text::create(document(), child.hasLeadingWhitespace ? makeString('\n', child.text) : child.text));
- lineElements.children.uncheckedAppend(WTFMove(textContainer));
- }
-
- lineContainer->appendChild(HTMLBRElement::create(document()));
- textRecognitionElements.lines.uncheckedAppend(WTFMove(lineElements));
- }
-
-#if ENABLE(DATA_DETECTION)
- textRecognitionElements.dataDetectors.reserveInitialCapacity(result.dataDetectors.size());
- for (auto& dataDetector : result.dataDetectors) {
- auto dataDetectorContainer = DataDetection::createElementForImageOverlay(document(), dataDetector);
- dataDetectorContainer->classList().add(imageOverlayDataDetectorClassName());
- rootContainer->appendChild(dataDetectorContainer);
- textRecognitionElements.dataDetectors.uncheckedAppend(WTFMove(dataDetectorContainer));
- }
-#endif // ENABLE(DATA_DETECTION)
-
- if (document().quirks().needsToForceUserSelectWhenInstallingImageOverlay())
- setInlineStyleProperty(CSSPropertyWebkitUserSelect, CSSValueText);
- }
-
- if (!hadExistingTextRecognitionElements) {
- static MainThreadNeverDestroyed<const String> shadowStyle(StringImpl::createWithoutCopying(imageOverlayUserAgentStyleSheet, sizeof(imageOverlayUserAgentStyleSheet)));
- auto style = HTMLStyleElement::create(HTMLNames::styleTag, document(), false);
- style->setTextContent(shadowStyle);
- shadowRoot->appendChild(WTFMove(style));
- }
-
- document().updateLayoutIgnorePendingStylesheets();
-
- auto* renderer = this->renderer();
- if (!is<RenderImage>(renderer))
- return;
-
- downcast<RenderImage>(*renderer).setHasImageOverlay();
-
- auto containerRect = containerRectForTextRecognition();
- auto convertToContainerCoordinates = [&](const FloatQuad& normalizedQuad) {
- auto quad = normalizedQuad;
- quad.scale(containerRect.width(), containerRect.height());
- quad.move(containerRect.x(), containerRect.y());
- return quad;
- };
-
- bool applyUserSelectAll = document().isImageDocument() || renderer->style().userSelect() != UserSelect::None;
- for (size_t lineIndex = 0; lineIndex < result.lines.size(); ++lineIndex) {
- auto& lineElements = textRecognitionElements.lines[lineIndex];
- auto& lineContainer = lineElements.line;
- auto& line = result.lines[lineIndex];
- auto lineQuad = convertToContainerCoordinates(line.normalizedQuad);
- if (lineQuad.isEmpty())
- continue;
-
- auto lineBounds = rotatedBoundingRectWithMinimumAngleOfRotation(lineQuad, 0.01);
- lineContainer->setInlineStyleProperty(CSSPropertyWidth, lineBounds.size.width(), CSSUnitType::CSS_PX);
- lineContainer->setInlineStyleProperty(CSSPropertyHeight, lineBounds.size.height(), CSSUnitType::CSS_PX);
- lineContainer->setInlineStyleProperty(CSSPropertyTransform, makeString(
- "translate("_s,
- std::round(lineBounds.center.x() - (lineBounds.size.width() / 2)), "px, "_s,
- std::round(lineBounds.center.y() - (lineBounds.size.height() / 2)), "px) "_s,
- lineBounds.angleInRadians ? makeString("rotate("_s, lineBounds.angleInRadians, "rad) "_s) : emptyString()
- ));
-
- auto offsetAlongHorizontalAxis = [&](const FloatPoint& quadPoint1, const FloatPoint& quadPoint2) {
- auto intervalLength = lineBounds.size.width();
- auto mid = midPoint(quadPoint1, quadPoint2);
- mid.moveBy(-lineBounds.center);
- mid.rotate(-lineBounds.angleInRadians);
- return intervalLength * clampTo<float>(0.5 + mid.x() / intervalLength, 0, 1);
- };
-
- auto offsetsAlongHorizontalAxis = line.children.map([&](auto& child) -> WTF::Range<float> {
- auto textQuad = convertToContainerCoordinates(child.normalizedQuad);
- return {
- offsetAlongHorizontalAxis(textQuad.p1(), textQuad.p4()),
- offsetAlongHorizontalAxis(textQuad.p2(), textQuad.p3())
- };
- });
-
- for (size_t childIndex = 0; childIndex < line.children.size(); ++childIndex) {
- auto& textContainer = lineElements.children[childIndex];
- bool lineHasOneChild = line.children.size() == 1;
- float horizontalMarginToMinimizeSelectionGaps = lineHasOneChild ? 0 : 0.125;
- float horizontalOffset = lineHasOneChild ? 0 : -horizontalMarginToMinimizeSelectionGaps;
- float horizontalExtent = lineHasOneChild ? 0 : horizontalMarginToMinimizeSelectionGaps;
-
- if (lineHasOneChild) {
- horizontalOffset += offsetsAlongHorizontalAxis[childIndex].begin();
- horizontalExtent += offsetsAlongHorizontalAxis[childIndex].end();
- } else if (!childIndex) {
- horizontalOffset += offsetsAlongHorizontalAxis[childIndex].begin();
- horizontalExtent += (offsetsAlongHorizontalAxis[childIndex].end() + offsetsAlongHorizontalAxis[childIndex + 1].begin()) / 2;
- } else if (childIndex == line.children.size() - 1) {
- horizontalOffset += (offsetsAlongHorizontalAxis[childIndex - 1].end() + offsetsAlongHorizontalAxis[childIndex].begin()) / 2;
- horizontalExtent += offsetsAlongHorizontalAxis[childIndex].end();
- } else {
- horizontalOffset += (offsetsAlongHorizontalAxis[childIndex - 1].end() + offsetsAlongHorizontalAxis[childIndex].begin()) / 2;
- horizontalExtent += (offsetsAlongHorizontalAxis[childIndex].end() + offsetsAlongHorizontalAxis[childIndex + 1].begin()) / 2;
- }
-
- FloatSize targetSize { horizontalExtent - horizontalOffset, lineBounds.size.height() };
- if (targetSize.isEmpty()) {
- textContainer->setInlineStyleProperty(CSSPropertyTransform, "scale(0, 0)");
- continue;
- }
-
- document().updateLayoutIfDimensionsOutOfDate(textContainer);
-
- FloatSize sizeBeforeTransform;
- if (auto* renderer = textContainer->renderBoxModelObject()) {
- sizeBeforeTransform = {
- adjustLayoutUnitForAbsoluteZoom(renderer->offsetWidth(), *renderer).toFloat(),
- adjustLayoutUnitForAbsoluteZoom(renderer->offsetHeight(), *renderer).toFloat(),
- };
- }
-
- if (sizeBeforeTransform.isEmpty()) {
- textContainer->setInlineStyleProperty(CSSPropertyTransform, "scale(0, 0)");
- continue;
- }
-
- textContainer->setInlineStyleProperty(CSSPropertyTransform, makeString(
- "translate("_s,
- horizontalOffset + (targetSize.width() - sizeBeforeTransform.width()) / 2, "px, "_s,
- (targetSize.height() - sizeBeforeTransform.height()) / 2, "px) "_s,
- "scale("_s, targetSize.width() / sizeBeforeTransform.width(), ", "_s, targetSize.height() / sizeBeforeTransform.height(), ") "_s
- ));
-
- textContainer->setInlineStyleProperty(CSSPropertyWebkitUserSelect, applyUserSelectAll ? CSSValueAll : CSSValueNone);
- }
-
- if (document().isImageDocument())
- lineContainer->setInlineStyleProperty(CSSPropertyCursor, CSSValueText);
- }
-
-#if ENABLE(DATA_DETECTION)
- for (size_t index = 0; index < result.dataDetectors.size(); ++index) {
- auto dataDetectorContainer = textRecognitionElements.dataDetectors[index];
- auto& dataDetector = result.dataDetectors[index];
- if (dataDetector.normalizedQuads.isEmpty())
- continue;
-
- // FIXME: We should come up with a way to coalesce the bounding quads into one or more rotated rects with the same angle of rotation.
- auto targetQuad = convertToContainerCoordinates(dataDetector.normalizedQuads.first());
- auto targetBounds = rotatedBoundingRectWithMinimumAngleOfRotation(targetQuad, 0.01);
- dataDetectorContainer->setInlineStyleProperty(CSSPropertyWidth, targetBounds.size.width(), CSSUnitType::CSS_PX);
- dataDetectorContainer->setInlineStyleProperty(CSSPropertyHeight, targetBounds.size.height(), CSSUnitType::CSS_PX);
- dataDetectorContainer->setInlineStyleProperty(CSSPropertyTransform, makeString(
- "translate("_s,
- std::round(targetBounds.center.x() - (targetBounds.size.width() / 2)), "px, "_s,
- std::round(targetBounds.center.y() - (targetBounds.size.height() / 2)), "px) "_s,
- targetBounds.angleInRadians ? makeString("rotate("_s, targetBounds.angleInRadians, "rad) "_s) : emptyString()
- ));
- }
-#endif // ENABLE(DATA_DETECTION)
-
- if (RefPtr frame = document().frame())
- frame->eventHandler().scheduleCursorUpdate();
-
- if (cacheTextRecognitionResults == CacheTextRecognitionResults::Yes) {
- if (auto* page = document().page())
- page->cacheTextRecognitionResult(*this, containerRect, result);
- }
-}
-
-#endif // ENABLE(IMAGE_ANALYSIS)
-
#if PLATFORM(IOS_FAMILY)
SelectionRenderingBehavior HTMLElement::selectionRenderingBehavior(const Node* node)
{
- return isImageOverlayText(node) ? SelectionRenderingBehavior::UseIndividualQuads : SelectionRenderingBehavior::CoalesceBoundingRects;
+ return ImageOverlay::isOverlayText(node) ? SelectionRenderingBehavior::UseIndividualQuads : SelectionRenderingBehavior::CoalesceBoundingRects;
}
#endif // PLATFORM(IOS_FAMILY)
Modified: trunk/Source/WebCore/html/HTMLElement.h (285654 => 285655)
--- trunk/Source/WebCore/html/HTMLElement.h 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/html/HTMLElement.h 2021-11-11 21:52:01 UTC (rev 285655)
@@ -132,20 +132,7 @@
void setEnterKeyHint(const String& value);
WEBCORE_EXPORT static bool shouldExtendSelectionToTargetNode(const Node& targetNode, const VisibleSelection& selectionBeforeUpdate);
- WEBCORE_EXPORT bool hasImageOverlay() const;
- WEBCORE_EXPORT bool isImageOverlayDataDetectorResult() const;
- WEBCORE_EXPORT static bool isInsideImageOverlay(const SimpleRange&);
- WEBCORE_EXPORT static bool isInsideImageOverlay(const Node&);
- WEBCORE_EXPORT static bool isImageOverlayText(const Node&);
- WEBCORE_EXPORT static bool isImageOverlayText(const Node*);
- void removeImageOverlaySoonIfNeeded();
-#if ENABLE(IMAGE_ANALYSIS)
- IntRect containerRectForTextRecognition();
- enum class CacheTextRecognitionResults : bool { No, Yes };
- WEBCORE_EXPORT void updateWithTextRecognitionResult(const TextRecognitionResult&, CacheTextRecognitionResults = CacheTextRecognitionResults::Yes);
-#endif
-
#if PLATFORM(IOS_FAMILY)
static SelectionRenderingBehavior selectionRenderingBehavior(const Node*);
#endif
Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (285654 => 285655)
--- trunk/Source/WebCore/html/HTMLMediaElement.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -61,6 +61,7 @@
#include "HTMLSourceElement.h"
#include "HTMLTrackElement.h"
#include "HTMLVideoElement.h"
+#include "ImageOverlay.h"
#include "InbandGenericTextTrack.h"
#include "InbandTextTrackPrivate.h"
#include "InbandWebVTTTextTrack.h"
@@ -3140,7 +3141,7 @@
if (processingUserGestureForMedia())
mediaSession().removeBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
- removeImageOverlaySoonIfNeeded();
+ ImageOverlay::removeOverlaySoonIfNeeded(*this);
}
void HTMLMediaElement::seekTask()
@@ -3734,7 +3735,7 @@
m_autoplaying = false;
updatePlayState();
- removeImageOverlaySoonIfNeeded();
+ ImageOverlay::removeOverlaySoonIfNeeded(*this);
}
void HTMLMediaElement::pause()
Modified: trunk/Source/WebCore/page/ContextMenuController.cpp (285654 => 285655)
--- trunk/Source/WebCore/page/ContextMenuController.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/page/ContextMenuController.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -51,6 +51,7 @@
#include "HTMLFormControlElement.h"
#include "HTMLFormElement.h"
#include "HitTestResult.h"
+#include "ImageOverlay.h"
#include "InspectorController.h"
#include "LocalizedStrings.h"
#include "MouseEvent.h"
@@ -943,7 +944,7 @@
}
auto selectedRange = frame->selection().selection().range();
- bool selectionIsInsideImageOverlay = selectedRange && HTMLElement::isInsideImageOverlay(*selectedRange);
+ bool selectionIsInsideImageOverlay = selectedRange && ImageOverlay::isInsideOverlay(*selectedRange);
bool shouldShowItemsForNonEditableText = ([&] {
if (!linkURL.isEmpty())
return false;
Modified: trunk/Source/WebCore/page/DragController.cpp (285654 => 285655)
--- trunk/Source/WebCore/page/DragController.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/page/DragController.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -67,6 +67,7 @@
#include "HitTestResult.h"
#include "Image.h"
#include "ImageOrientation.h"
+#include "ImageOverlay.h"
#include "Model.h"
#include "MoveSelectionCommand.h"
#include "Page.h"
@@ -799,7 +800,7 @@
#endif
auto selectionDragElement = state.type.contains(DragSourceAction::Selection) && m_dragSourceAction.contains(DragSourceAction::Selection) ? startElement : nullptr;
- if (HTMLElement::isImageOverlayText(startElement))
+ if (ImageOverlay::isOverlayText(startElement))
return selectionDragElement;
for (auto* element = startElement; element; element = element->parentOrShadowHostElement()) {
@@ -1041,7 +1042,7 @@
src.editor().willWriteSelectionToPasteboard(*selectionRange);
auto selection = src.selection().selection();
bool shouldDragAsPlainText = enclosingTextFormControl(selection.start());
- if (auto range = selection.range(); range && HTMLElement::isInsideImageOverlay(*range))
+ if (auto range = selection.range(); range && ImageOverlay::isInsideOverlay(*range))
shouldDragAsPlainText = true;
if (shouldDragAsPlainText) {
Modified: trunk/Source/WebCore/page/EventHandler.cpp (285654 => 285655)
--- trunk/Source/WebCore/page/EventHandler.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/page/EventHandler.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -69,6 +69,7 @@
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "Image.h"
+#include "ImageOverlay.h"
#include "ImageOverlayController.h"
#include "InspectorInstrumentation.h"
#include "KeyboardEvent.h"
@@ -449,7 +450,7 @@
static Node* nodeToSelectOnMouseDownForNode(Node& targetNode)
{
- if (HTMLElement::isInsideImageOverlay(targetNode))
+ if (ImageOverlay::isInsideOverlay(targetNode))
return nullptr;
if (RefPtr rootUserSelectAll = Position::rootUserSelectAllForNode(&targetNode))
@@ -720,7 +721,7 @@
if (!node || !node->renderer())
return true;
- if (HTMLElement::isImageOverlayText(*node))
+ if (ImageOverlay::isOverlayText(*node))
return node->renderer()->style().userSelectIncludingInert() != UserSelect::None;
return node->canStartSelection() || Position::nodeIsUserSelectAll(node.get());
@@ -766,7 +767,7 @@
// Bug: https://bugs.webkit.org/show_bug.cgi?id=155390
// Single mouse down on links or images can always trigger drag-n-drop.
- bool isImageOverlayText = HTMLElement::isImageOverlayText(event.targetNode());
+ bool isImageOverlayText = ImageOverlay::isOverlayText(event.targetNode());
bool isMouseDownOnLinkOrImage = event.isOverLink() || (event.hitTestResult().image() && !isImageOverlayText);
m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage) && shouldAllowMouseDownToStartDrag();
#endif
@@ -1023,7 +1024,7 @@
m_frame.selection().setSelectionByMouseIfDifferent(newSelection, m_frame.selection().granularity(),
FrameSelection::EndPointsAdjustmentMode::AdjustAtBidiBoundary);
- if (oldSelection != newSelection && HTMLElement::isImageOverlayText(newSelection.start().containerNode()) && HTMLElement::isImageOverlayText(newSelection.end().containerNode()))
+ if (oldSelection != newSelection && ImageOverlay::isOverlayText(newSelection.start().containerNode()) && ImageOverlay::isOverlayText(newSelection.end().containerNode()))
invalidateClick();
}
#endif // ENABLE(DRAG_SUPPORT)
@@ -1200,7 +1201,7 @@
RefPtr innerNode = result.innerNode();
if (request.disallowsUserAgentShadowContent()
- || (request.disallowsUserAgentShadowContentExceptForImageOverlays() && innerNode && !HTMLElement::isInsideImageOverlay(*innerNode)))
+ || (request.disallowsUserAgentShadowContentExceptForImageOverlays() && innerNode && !ImageOverlay::isInsideOverlay(*innerNode)))
result.setToNonUserAgentShadowAncestor();
return result;
@@ -1521,7 +1522,7 @@
switch (style ? style->cursor() : CursorType::Auto) {
case CursorType::Auto: {
- if (HTMLElement::isImageOverlayText(node.get())) {
+ if (ImageOverlay::isOverlayText(node.get())) {
auto* renderer = node->renderer();
if (renderer && renderer->style().userSelectIncludingInert() != UserSelect::None)
return iBeam;
Modified: trunk/Source/WebCore/page/ImageOverlayController.cpp (285654 => 285655)
--- trunk/Source/WebCore/page/ImageOverlayController.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/page/ImageOverlayController.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -34,6 +34,7 @@
#include "FrameSelection.h"
#include "GraphicsContext.h"
#include "HTMLElement.h"
+#include "ImageOverlay.h"
#include "IntRect.h"
#include "LayoutRect.h"
#include "Page.h"
@@ -71,7 +72,7 @@
if (!selectedRange)
return nullptr;
- if (!HTMLElement::isInsideImageOverlay(*selectedRange))
+ if (!ImageOverlay::isInsideOverlay(*selectedRange))
return nullptr;
if (RefPtr host = selectedRange->startContainer().shadowHost(); is<HTMLElement>(host))
Modified: trunk/Source/WebCore/page/Page.cpp (285654 => 285655)
--- trunk/Source/WebCore/page/Page.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/page/Page.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -76,6 +76,7 @@
#include "HistoryController.h"
#include "HistoryItem.h"
#include "IDBConnectionToServer.h"
+#include "ImageOverlay.h"
#include "ImageOverlayController.h"
#include "InspectorClient.h"
#include "InspectorController.h"
@@ -3723,7 +3724,7 @@
continue;
auto& [result, containerRect] = entry.value;
- auto newContainerRect = protectedElement->containerRectForTextRecognition();
+ auto newContainerRect = ImageOverlay::containerRect(protectedElement.get());
if (containerRect == newContainerRect)
continue;
@@ -3733,11 +3734,8 @@
for (auto& [element, result] : elementsToUpdate) {
element->document().eventLoop().queueTask(TaskSource::InternalAsyncTask, [result = TextRecognitionResult { result }, weakElement = WeakPtr { element }] {
- RefPtr element { weakElement.get() };
- if (!element)
- return;
-
- element->updateWithTextRecognitionResult(result, HTMLElement::CacheTextRecognitionResults::No);
+ if (RefPtr element = weakElement.get())
+ ImageOverlay::updateWithTextRecognitionResult(*element, result, ImageOverlay::CacheTextRecognitionResults::No);
});
}
}
Modified: trunk/Source/WebCore/page/mac/ImageOverlayControllerMac.mm (285654 => 285655)
--- trunk/Source/WebCore/page/mac/ImageOverlayControllerMac.mm 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/page/mac/ImageOverlayControllerMac.mm 2021-11-11 21:52:01 UTC (rev 285655)
@@ -35,6 +35,7 @@
#import "FrameView.h"
#import "HTMLElement.h"
#import "HTMLNames.h"
+#import "ImageOverlay.h"
#import "ImageOverlayDataDetectionResultIdentifier.h"
#import "IntRect.h"
#import "Page.h"
@@ -50,7 +51,7 @@
void ImageOverlayController::updateDataDetectorHighlights(const HTMLElement& overlayHost)
{
- if (!overlayHost.hasImageOverlay()) {
+ if (!ImageOverlay::hasOverlay(overlayHost)) {
ASSERT_NOT_REACHED();
clearDataDetectorHighlights();
return;
@@ -58,7 +59,7 @@
Vector<Ref<HTMLElement>> dataDetectorResultElements;
for (auto& child : descendantsOfType<HTMLElement>(*overlayHost.userAgentShadowRoot())) {
- if (child.isImageOverlayDataDetectorResult() && child.renderer())
+ if (ImageOverlay::isDataDetectorResult(child) && child.renderer())
dataDetectorResultElements.append(child);
}
@@ -190,7 +191,7 @@
if (!elementUnderMouse && m_hostElementForDataDetectors && frame.document() != &m_hostElementForDataDetectors->document())
return;
- if (!elementUnderMouse || !HTMLElement::isInsideImageOverlay(*elementUnderMouse)) {
+ if (!elementUnderMouse || !ImageOverlay::isInsideOverlay(*elementUnderMouse)) {
m_hostElementForDataDetectors = nullptr;
uninstallPageOverlayIfNeeded();
return;
@@ -205,7 +206,7 @@
}
Ref imageOverlayHost = downcast<HTMLElement>(*shadowHost);
- if (!imageOverlayHost->hasImageOverlay()) {
+ if (!ImageOverlay::hasOverlay(imageOverlayHost.get())) {
ASSERT_NOT_REACHED();
m_hostElementForDataDetectors = nullptr;
uninstallPageOverlayIfNeeded();
Modified: trunk/Source/WebCore/rendering/HitTestResult.cpp (285654 => 285655)
--- trunk/Source/WebCore/rendering/HitTestResult.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/rendering/HitTestResult.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -38,6 +38,7 @@
#include "HTMLParserIdioms.h"
#include "HTMLTextAreaElement.h"
#include "HTMLVideoElement.h"
+#include "ImageOverlay.h"
#include "PseudoElement.h"
#include "Range.h"
#include "RenderBlockFlow.h"
@@ -332,7 +333,7 @@
if (!m_innerNonSharedNode)
return nullptr;
- if (HTMLElement::isInsideImageOverlay(*m_innerNonSharedNode))
+ if (ImageOverlay::isInsideOverlay(*m_innerNonSharedNode))
return m_innerNonSharedNode->shadowHost();
return m_innerNonSharedNode;
@@ -653,7 +654,7 @@
return HitTestProgress::Continue;
if ((request.disallowsUserAgentShadowContent() && node->isInUserAgentShadowTree())
- || (request.disallowsUserAgentShadowContentExceptForImageOverlays() && !HTMLElement::isInsideImageOverlay(*node) && node->isInUserAgentShadowTree()))
+ || (request.disallowsUserAgentShadowContentExceptForImageOverlays() && !ImageOverlay::isInsideOverlay(*node) && node->isInUserAgentShadowTree()))
node = node->document().ancestorNodeInThisScope(node);
mutableListBasedTestResult().add(*node);
Modified: trunk/Source/WebCore/rendering/RenderImage.cpp (285654 => 285655)
--- trunk/Source/WebCore/rendering/RenderImage.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/rendering/RenderImage.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -45,6 +45,7 @@
#include "HTMLMapElement.h"
#include "HTMLNames.h"
#include "HitTestResult.h"
+#include "ImageOverlay.h"
#include "InlineIteratorInlineBox.h"
#include "InlineIteratorLine.h"
#include "Page.h"
@@ -140,7 +141,7 @@
RenderImage::RenderImage(Element& element, RenderStyle&& style, StyleImage* styleImage, const float imageDevicePixelRatio)
: RenderReplaced(element, WTFMove(style), IntSize())
, m_imageResource(styleImage ? makeUnique<RenderImageResourceStyleImage>(*styleImage) : makeUnique<RenderImageResource>())
- , m_hasImageOverlay(is<HTMLElement>(element) && downcast<HTMLElement>(element).hasImageOverlay())
+ , m_hasImageOverlay(is<HTMLElement>(element) && ImageOverlay::hasOverlay(downcast<HTMLElement>(element)))
, m_imageDevicePixelRatio(imageDevicePixelRatio)
{
updateAltText();
Modified: trunk/Source/WebCore/testing/Internals.cpp (285654 => 285655)
--- trunk/Source/WebCore/testing/Internals.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebCore/testing/Internals.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -111,6 +111,7 @@
#include "HitTestResult.h"
#include "IDBRequest.h"
#include "IDBTransaction.h"
+#include "ImageOverlay.h"
#include "InspectorClient.h"
#include "InspectorController.h"
#include "InspectorDebuggableType.h"
@@ -5807,7 +5808,7 @@
return;
#if ENABLE(IMAGE_ANALYSIS)
- downcast<HTMLElement>(element).updateWithTextRecognitionResult(TextRecognitionResult {
+ ImageOverlay::updateWithTextRecognitionResult(downcast<HTMLElement>(element), TextRecognitionResult {
lines.map([] (auto& line) -> TextRecognitionLineData {
return makeDataForLine(line);
})
Modified: trunk/Source/WebKit/ChangeLog (285654 => 285655)
--- trunk/Source/WebKit/ChangeLog 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebKit/ChangeLog 2021-11-11 21:52:01 UTC (rev 285655)
@@ -1,3 +1,31 @@
+2021-11-11 Wenson Hsieh <[email protected]>
+
+ Move image overlay code out of HTMLElement and into a separate helper file
+ https://bugs.webkit.org/show_bug.cgi?id=232974
+
+ Reviewed by Antti Koivisto.
+
+ See WebCore/ChangeLog for more details.
+
+ * WebProcess/WebPage/Cocoa/WebPageCocoa.mm:
+ (WebKit::WebPage::dictionaryPopupInfoForRange):
+ * WebProcess/WebPage/FindController.cpp:
+ (WebKit::FindController::updateFindIndicator):
+ * WebProcess/WebPage/WebPage.cpp:
+ (WebKit::WebPage::editorState const):
+ (WebKit::WebPage::findDataDetectionResultElementInImageOverlay):
+ (WebKit::WebPage::requestTextRecognition):
+ (WebKit::WebPage::updateWithTextRecognitionResult):
+ * WebProcess/WebPage/ios/FindControllerIOS.mm:
+ (WebKit::findTextIndicatorOptions):
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::insideImageOverlay):
+ (WebKit::rangeForPointInRootViewCoordinates):
+ (WebKit::hostVideoElementIgnoringImageOverlay):
+ (WebKit::elementPositionInformation):
+ * WebProcess/WebPage/mac/WebPageMac.mm:
+ (WebKit::WebPage::performImmediateActionHitTestAtLocation):
+
2021-11-11 Ben Nham <[email protected]>
Log memory usage metadata when WebContent crosses critical or warning level memory thresholds
Modified: trunk/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm (285654 => 285655)
--- trunk/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm 2021-11-11 21:52:01 UTC (rev 285655)
@@ -47,6 +47,7 @@
#import <WebCore/HTMLOListElement.h>
#import <WebCore/HTMLUListElement.h>
#import <WebCore/HitTestResult.h>
+#import <WebCore/ImageOverlay.h>
#import <WebCore/NetworkExtensionContentFilter.h>
#import <WebCore/NodeRenderStyle.h>
#import <WebCore/PaymentCoordinator.h>
@@ -200,7 +201,7 @@
#endif // PLATFORM(MAC)
OptionSet<TextIndicatorOption> indicatorOptions { TextIndicatorOption::UseBoundingRectAndPaintAllContentForComplexRanges };
- if (HTMLElement::isInsideImageOverlay(range))
+ if (ImageOverlay::isInsideOverlay(range))
indicatorOptions.add({ TextIndicatorOption::PaintAllContent, TextIndicatorOption::PaintBackgrounds });
if (presentationTransition == TextIndicatorPresentationTransition::BounceAndCrossfade)
Modified: trunk/Source/WebKit/WebProcess/WebPage/FindController.cpp (285654 => 285655)
--- trunk/Source/WebKit/WebProcess/WebPage/FindController.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebKit/WebProcess/WebPage/FindController.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -40,6 +40,7 @@
#include <WebCore/FrameSelection.h>
#include <WebCore/FrameView.h>
#include <WebCore/GraphicsContext.h>
+#include <WebCore/ImageOverlay.h>
#include <WebCore/Page.h>
#include <WebCore/PageOverlayController.h>
#include <WebCore/PathUtilities.h>
@@ -374,7 +375,7 @@
bool FindController::updateFindIndicator(Frame& selectedFrame, bool isShowingOverlay, bool shouldAnimate)
{
OptionSet<TextIndicatorOption> textIndicatorOptions { TextIndicatorOption::IncludeMarginIfRangeMatchesSelection };
- if (auto selectedRange = selectedFrame.selection().selection().range(); selectedRange && HTMLElement::isInsideImageOverlay(*selectedRange))
+ if (auto selectedRange = selectedFrame.selection().selection().range(); selectedRange && ImageOverlay::isInsideOverlay(*selectedRange))
textIndicatorOptions.add({ TextIndicatorOption::PaintAllContent, TextIndicatorOption::PaintBackgrounds });
auto indicator = TextIndicator::createWithSelectionInFrame(selectedFrame, textIndicatorOptions, shouldAnimate ? TextIndicatorPresentationTransition::Bounce : TextIndicatorPresentationTransition::None);
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (285654 => 285655)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2021-11-11 21:52:01 UTC (rev 285655)
@@ -202,6 +202,7 @@
#include <WebCore/HistoryController.h>
#include <WebCore/HistoryItem.h>
#include <WebCore/HitTestResult.h>
+#include <WebCore/ImageOverlay.h>
#include <WebCore/InspectorController.h>
#include <WebCore/JSDOMExceptionHandling.h>
#include <WebCore/JSDOMWindow.h>
@@ -1213,7 +1214,7 @@
if (result.selectionIsRange) {
auto selectionRange = selection.range();
- result.selectionIsRangeInsideImageOverlay = selectionRange && HTMLElement::isInsideImageOverlay(*selectionRange);
+ result.selectionIsRangeInsideImageOverlay = selectionRange && ImageOverlay::isInsideOverlay(*selectionRange);
}
m_lastEditorStateWasContentEditable = result.isContentEditable ? EditorStateIsContentEditable::Yes : EditorStateIsContentEditable::No;
@@ -4121,7 +4122,7 @@
{
Vector<Ref<HTMLElement>> dataDetectorElements;
for (auto& child : descendantsOfType<HTMLElement>(*imageOverlayHost.shadowRoot())) {
- if (child.isImageOverlayDataDetectorResult())
+ if (ImageOverlay::isDataDetectorResult(child))
dataDetectorElements.append(child);
}
@@ -7497,7 +7498,7 @@
if (corePage()->hasCachedTextRecognitionResult(htmlElement.get())) {
if (completion) {
RefPtr<Element> imageOverlayHost;
- if (htmlElement->hasImageOverlay())
+ if (ImageOverlay::hasOverlay(htmlElement.get()))
imageOverlayHost = &element;
completion(WTFMove(imageOverlayHost));
}
@@ -7564,7 +7565,7 @@
return;
auto& htmlElement = downcast<HTMLElement>(*protectedElement);
- htmlElement.updateWithTextRecognitionResult(result);
+ ImageOverlay::updateWithTextRecognitionResult(htmlElement, result);
auto matchIndex = protectedPage->m_elementsPendingTextRecognition.findMatching([&] (auto& elementAndCompletionHandlers) {
return elementAndCompletionHandlers.first == &htmlElement;
@@ -7573,7 +7574,7 @@
if (matchIndex == notFound)
return;
- RefPtr imageOverlayHost = htmlElement.hasImageOverlay() ? &htmlElement : nullptr;
+ RefPtr imageOverlayHost = ImageOverlay::hasOverlay(htmlElement) ? &htmlElement : nullptr;
for (auto& completionHandler : protectedPage->m_elementsPendingTextRecognition[matchIndex].second)
completionHandler(imageOverlayHost.copyRef());
@@ -7589,7 +7590,7 @@
return;
}
- downcast<HTMLElement>(*elementToUpdate).updateWithTextRecognitionResult(result);
+ ImageOverlay::updateWithTextRecognitionResult(downcast<HTMLElement>(*elementToUpdate), result);
auto hitTestResult = corePage()->mainFrame().eventHandler().hitTestResultAtPoint(roundedIntPoint(location), {
HitTestRequest::Type::ReadOnly,
HitTestRequest::Type::Active,
@@ -7598,7 +7599,7 @@
RefPtr nodeAtLocation = hitTestResult.innerNonSharedNode();
auto updateResult = ([&] {
- if (!nodeAtLocation || nodeAtLocation->shadowHost() != elementToUpdate || !HTMLElement::isInsideImageOverlay(*nodeAtLocation))
+ if (!nodeAtLocation || nodeAtLocation->shadowHost() != elementToUpdate || !ImageOverlay::isInsideOverlay(*nodeAtLocation))
return TextRecognitionUpdateResult::NoText;
#if ENABLE(DATA_DETECTION)
@@ -7606,7 +7607,7 @@
return TextRecognitionUpdateResult::DataDetector;
#endif
- if (HTMLElement::isImageOverlayText(*nodeAtLocation))
+ if (ImageOverlay::isOverlayText(*nodeAtLocation))
return TextRecognitionUpdateResult::Text;
return TextRecognitionUpdateResult::NoText;
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/FindControllerIOS.mm (285654 => 285655)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/FindControllerIOS.mm 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/FindControllerIOS.mm 2021-11-11 21:52:01 UTC (rev 285655)
@@ -38,6 +38,7 @@
#import <WebCore/Frame.h>
#import <WebCore/FrameView.h>
#import <WebCore/GraphicsContext.h>
+#import <WebCore/ImageOverlay.h>
#import <WebCore/Page.h>
#import <WebCore/PageOverlayController.h>
#import <WebCore/PathUtilities.h>
@@ -54,7 +55,7 @@
static OptionSet<TextIndicatorOption> findTextIndicatorOptions(const Frame& frame)
{
OptionSet<TextIndicatorOption> options { TextIndicatorOption::IncludeMarginIfRangeMatchesSelection, TextIndicatorOption::DoNotClipToVisibleRect };
- if (auto selectedRange = frame.selection().selection().range(); selectedRange && HTMLElement::isInsideImageOverlay(*selectedRange))
+ if (auto selectedRange = frame.selection().selection().range(); selectedRange && ImageOverlay::isInsideOverlay(*selectedRange))
options.add({ TextIndicatorOption::PaintAllContent, TextIndicatorOption::PaintBackgrounds });
return options;
};
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (285654 => 285655)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2021-11-11 21:52:01 UTC (rev 285655)
@@ -108,6 +108,7 @@
#import <WebCore/HTMLVideoElement.h>
#import <WebCore/HistoryItem.h>
#import <WebCore/HitTestResult.h>
+#import <WebCore/ImageOverlay.h>
#import <WebCore/InputMode.h>
#import <WebCore/KeyboardEvent.h>
#import <WebCore/LibWebRTCProvider.h>
@@ -1349,7 +1350,7 @@
static bool insideImageOverlay(const VisiblePosition& position)
{
RefPtr container = position.deepEquivalent().containerNode();
- return container && HTMLElement::isInsideImageOverlay(*container);
+ return container && ImageOverlay::isInsideOverlay(*container);
}
static std::optional<SimpleRange> expandForImageOverlay(const SimpleRange& range)
@@ -1586,7 +1587,7 @@
range = makeSimpleRange(result, selectionEnd);
}
- if (range && HTMLElement::isInsideImageOverlay(*range))
+ if (range && ImageOverlay::isInsideOverlay(*range))
return { expandForImageOverlay(*range), SelectionWasFlipped::No };
return { range, selectionFlipped };
@@ -2805,7 +2806,7 @@
static RefPtr<HTMLVideoElement> hostVideoElementIgnoringImageOverlay(Node& node)
{
- if (HTMLElement::isInsideImageOverlay(node))
+ if (ImageOverlay::isInsideOverlay(node))
return { };
if (is<HTMLVideoElement>(node))
@@ -2858,7 +2859,7 @@
info.isElement = true;
info.idAttribute = element.getIdAttribute();
- info.isImageOverlayText = HTMLElement::isImageOverlayText(innerNonSharedNode);
+ info.isImageOverlayText = ImageOverlay::isOverlayText(innerNonSharedNode);
info.title = element.attributeWithoutSynchronization(HTMLNames::titleAttr).string();
if (linkElement && info.title.isEmpty())
@@ -2907,7 +2908,7 @@
#if ENABLE(DATA_DETECTION)
if (info.isImageOverlayText && innerNonSharedNode->shadowHost() == &element && is<HTMLElement>(element)) {
- if (Ref htmlElement = downcast<HTMLElement>(element); htmlElement->hasImageOverlay())
+ if (Ref htmlElement = downcast<HTMLElement>(element); ImageOverlay::hasOverlay(htmlElement.get()))
dataDetectorImageOverlayPositionInformation(htmlElement.get(), request, info);
}
#endif
Modified: trunk/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm (285654 => 285655)
--- trunk/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm 2021-11-11 21:29:00 UTC (rev 285654)
+++ trunk/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm 2021-11-11 21:52:01 UTC (rev 285655)
@@ -73,6 +73,7 @@
#import <WebCore/HTMLConverter.h>
#import <WebCore/HTMLPlugInImageElement.h>
#import <WebCore/HitTestResult.h>
+#import <WebCore/ImageOverlay.h>
#import <WebCore/KeyboardEvent.h>
#import <WebCore/MIMETypeRegistry.h>
#import <WebCore/NetworkStorageSession.h>
@@ -885,7 +886,7 @@
auto indicatorOptions = [&](const SimpleRange& range) {
OptionSet<TextIndicatorOption> options { TextIndicatorOption::UseBoundingRectAndPaintAllContentForComplexRanges };
- if (HTMLElement::isInsideImageOverlay(range))
+ if (ImageOverlay::isInsideOverlay(range))
options.add({ TextIndicatorOption::PaintAllContent, TextIndicatorOption::PaintBackgrounds });
return options;
};