Title: [290178] trunk
Revision
290178
Author
wenson_hs...@apple.com
Date
2022-02-18 15:50:27 -0800 (Fri, 18 Feb 2022)

Log Message

[Live Text] Add a mechanism to inject images into image overlays
https://bugs.webkit.org/show_bug.cgi?id=236842

Reviewed by Aditya Keerthi.

Source/WebCore:

To support additional behaviors around the "Copy Cropped Image" context menu item on macOS, add a new image
overlay helper method to install a temporary image element within the image overlay shadow root of a host image.
See below for more details.

Test: fast/images/text-recognition/image-overlay-cropped-image.html

* dom/ImageOverlay.cpp:
(WebCore::ImageOverlay::imageOverlayCroppedImageIdentifier):
(WebCore::ImageOverlay::imageOverlayCroppedImageBackdropIdentifier):
(WebCore::ImageOverlay::isOverlayText):
(WebCore::ImageOverlay::removeOverlaySoonIfNeeded):
(WebCore::ImageOverlay::installImageOverlayStyleSheet):

Pull out logic for installing the UA style sheet for image overlays into a helper method, so that we can ensure
the stylesheet in both cases where the `CroppedImage` is installed first, or Live Text is injected first.

(WebCore::ImageOverlay::updateSubtree):

Refactor this method to not completely remove the image overlay root container in the case where the Live Text
subtree needs to be regenerated; this prevents us from inadvertently removing the cropped image overlay elements
when updating Live Text elements in the UA shadow root.

(WebCore::ImageOverlay::CroppedImage::install):

Add a method to install a cropped image overlay into the given host element. This creates a new blob URL using
the given image data and MIME type, and uses it to insert an image element within the image overlay root
container that's absolutely positioned over the given crop rect (normalized relative to the image).

This inserted image is presented over another dimming element that fades in and out as `setVisibility()` is
called. Finally, `install` returns a `unique_ptr` to a RAII object that can be used to fade the cropped image
backdrop in or out; when destroyed, this RAII object immediately disconnects both the backdrop and the image
element, and revokes the temporary blob URL created during `install()`.

(WebCore::ImageOverlay::CroppedImage::CroppedImage):
(WebCore::ImageOverlay::CroppedImage::~CroppedImage):
(WebCore::ImageOverlay::CroppedImage::setVisibility):
* dom/ImageOverlay.h:
* dom/ShadowRoot.h:

Add a `using TreeScope::getElementById;` here to make it possible to call `getElementById` on `ShadowRoot`
without having to cast to a `TreeScope`.

* html/shadow/imageOverlay.css:
(img#image-overlay-cropped-image):
(div#image-overlay-cropped-image-backdrop):
* testing/Internals.cpp:
(WebCore::Internals::pngDataForTesting):
(WebCore::Internals::installCroppedImageOverlay):
(WebCore::Internals::uninstallCroppedImageOverlay):

Add internal testing support for the cropped image overlay by allowing layout tests to inject an image overlay
with hard-coded PNG data into the given element.

* testing/Internals.h:
* testing/Internals.idl:
* testing/Internals.mm:
(WebCore::Internals::pngDataForTesting):

LayoutTests:

Add a layout test that exercises the new cropped image overlay code.

* fast/images/text-recognition/image-overlay-cropped-image-expected.txt: Added.
* fast/images/text-recognition/image-overlay-cropped-image.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (290177 => 290178)


--- trunk/LayoutTests/ChangeLog	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/LayoutTests/ChangeLog	2022-02-18 23:50:27 UTC (rev 290178)
@@ -1,3 +1,15 @@
+2022-02-18  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [Live Text] Add a mechanism to inject images into image overlays
+        https://bugs.webkit.org/show_bug.cgi?id=236842
+
+        Reviewed by Aditya Keerthi.
+
+        Add a layout test that exercises the new cropped image overlay code.
+
+        * fast/images/text-recognition/image-overlay-cropped-image-expected.txt: Added.
+        * fast/images/text-recognition/image-overlay-cropped-image.html: Added.
+
 2022-02-18  Kimmo Kinnunen  <kkinnu...@apple.com>
 
         WebGL GPUP: Crash when running fast/mediastream/getUserMedia-to-canvas-1.html

Added: trunk/LayoutTests/fast/images/text-recognition/image-overlay-cropped-image-expected.txt (0 => 290178)


--- trunk/LayoutTests/fast/images/text-recognition/image-overlay-cropped-image-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/images/text-recognition/image-overlay-cropped-image-expected.txt	2022-02-18 23:50:27 UTC (rev 290178)
@@ -0,0 +1,13 @@
+
+PASS croppedImage is non-null.
+PASS croppedImageBackdrop is non-null.
+PASS croppedImageRect.x is 100
+PASS croppedImageRect.y is 100
+PASS croppedImageRect.width is 200
+PASS croppedImageRect.height is 200
+PASS croppedImage is null
+PASS croppedImageBackdrop is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/images/text-recognition/image-overlay-cropped-image.html (0 => 290178)


--- trunk/LayoutTests/fast/images/text-recognition/image-overlay-cropped-image.html	                        (rev 0)
+++ trunk/LayoutTests/fast/images/text-recognition/image-overlay-cropped-image.html	2022-02-18 23:50:27 UTC (rev 290178)
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style>
+body, html {
+    margin: 0;
+}
+</style>
+</head>
+<body>
+<img src=""
+<pre id="console"></pre>
+<script>
+addEventListener("load", () => {
+    const image = document.querySelector("img");
+
+    internals.installCroppedImageOverlay(image, new DOMRectReadOnly(0.25, 0.25, 0.5, 0.5));
+
+    croppedImage = internals.shadowRoot(image).getElementById("image-overlay-cropped-image");
+    croppedImageBackdrop = internals.shadowRoot(image).getElementById("image-overlay-cropped-image-backdrop");
+    shouldBeNonNull("croppedImage");
+    shouldBeNonNull("croppedImageBackdrop");
+
+    croppedImageRect = croppedImage.getBoundingClientRect();
+    shouldBe("croppedImageRect.x", "100");
+    shouldBe("croppedImageRect.y", "100");
+    shouldBe("croppedImageRect.width", "200");
+    shouldBe("croppedImageRect.height", "200");
+
+    internals.uninstallCroppedImageOverlay();
+
+    croppedImage = internals.shadowRoot(image).getElementById("image-overlay-cropped-image");
+    croppedImageBackdrop = internals.shadowRoot(image).getElementById("image-overlay-cropped-image-backdrop");
+    shouldBeNull("croppedImage");
+    shouldBeNull("croppedImageBackdrop");
+});
+</script>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/Source/WebCore/ChangeLog (290177 => 290178)


--- trunk/Source/WebCore/ChangeLog	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/ChangeLog	2022-02-18 23:50:27 UTC (rev 290178)
@@ -1,3 +1,68 @@
+2022-02-18  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [Live Text] Add a mechanism to inject images into image overlays
+        https://bugs.webkit.org/show_bug.cgi?id=236842
+
+        Reviewed by Aditya Keerthi.
+
+        To support additional behaviors around the "Copy Cropped Image" context menu item on macOS, add a new image
+        overlay helper method to install a temporary image element within the image overlay shadow root of a host image.
+        See below for more details.
+
+        Test: fast/images/text-recognition/image-overlay-cropped-image.html
+
+        * dom/ImageOverlay.cpp:
+        (WebCore::ImageOverlay::imageOverlayCroppedImageIdentifier):
+        (WebCore::ImageOverlay::imageOverlayCroppedImageBackdropIdentifier):
+        (WebCore::ImageOverlay::isOverlayText):
+        (WebCore::ImageOverlay::removeOverlaySoonIfNeeded):
+        (WebCore::ImageOverlay::installImageOverlayStyleSheet):
+
+        Pull out logic for installing the UA style sheet for image overlays into a helper method, so that we can ensure
+        the stylesheet in both cases where the `CroppedImage` is installed first, or Live Text is injected first.
+
+        (WebCore::ImageOverlay::updateSubtree):
+
+        Refactor this method to not completely remove the image overlay root container in the case where the Live Text
+        subtree needs to be regenerated; this prevents us from inadvertently removing the cropped image overlay elements
+        when updating Live Text elements in the UA shadow root.
+
+        (WebCore::ImageOverlay::CroppedImage::install):
+
+        Add a method to install a cropped image overlay into the given host element. This creates a new blob URL using
+        the given image data and MIME type, and uses it to insert an image element within the image overlay root
+        container that's absolutely positioned over the given crop rect (normalized relative to the image).
+
+        This inserted image is presented over another dimming element that fades in and out as `setVisibility()` is
+        called. Finally, `install` returns a `unique_ptr` to a RAII object that can be used to fade the cropped image
+        backdrop in or out; when destroyed, this RAII object immediately disconnects both the backdrop and the image
+        element, and revokes the temporary blob URL created during `install()`.
+
+        (WebCore::ImageOverlay::CroppedImage::CroppedImage):
+        (WebCore::ImageOverlay::CroppedImage::~CroppedImage):
+        (WebCore::ImageOverlay::CroppedImage::setVisibility):
+        * dom/ImageOverlay.h:
+        * dom/ShadowRoot.h:
+
+        Add a `using TreeScope::getElementById;` here to make it possible to call `getElementById` on `ShadowRoot`
+        without having to cast to a `TreeScope`.
+
+        * html/shadow/imageOverlay.css:
+        (img#image-overlay-cropped-image):
+        (div#image-overlay-cropped-image-backdrop):
+        * testing/Internals.cpp:
+        (WebCore::Internals::pngDataForTesting):
+        (WebCore::Internals::installCroppedImageOverlay):
+        (WebCore::Internals::uninstallCroppedImageOverlay):
+
+        Add internal testing support for the cropped image overlay by allowing layout tests to inject an image overlay
+        with hard-coded PNG data into the given element.
+
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        * testing/Internals.mm:
+        (WebCore::Internals::pngDataForTesting):
+
 2022-02-18  Kimmo Kinnunen  <kkinnu...@apple.com>
 
         WebGL GPUP: Crash when running fast/mediastream/getUserMedia-to-canvas-1.html

Modified: trunk/Source/WebCore/dom/ImageOverlay.cpp (290177 => 290178)


--- trunk/Source/WebCore/dom/ImageOverlay.cpp	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/dom/ImageOverlay.cpp	2022-02-18 23:50:27 UTC (rev 290178)
@@ -26,16 +26,20 @@
 #include "config.h"
 #include "ImageOverlay.h"
 
+#include "Blob.h"
 #include "CharacterRange.h"
 #include "DOMTokenList.h"
+#include "DOMURL.h"
 #include "Document.h"
 #include "ElementChildIterator.h"
 #include "EventHandler.h"
 #include "EventLoop.h"
+#include "FloatRect.h"
 #include "FloatSize.h"
 #include "GeometryUtilities.h"
 #include "HTMLBRElement.h"
 #include "HTMLDivElement.h"
+#include "HTMLImageElement.h"
 #include "HTMLMediaElement.h"
 #include "HTMLStyleElement.h"
 #include "ImageOverlayController.h"
@@ -45,6 +49,7 @@
 #include "RenderImage.h"
 #include "RenderText.h"
 #include "ShadowRoot.h"
+#include "SharedBuffer.h"
 #include "SimpleRange.h"
 #include "Text.h"
 #include "TextIterator.h"
@@ -96,6 +101,18 @@
 
 #endif // ENABLE(IMAGE_ANALYSIS)
 
+static const AtomString& imageOverlayCroppedImageIdentifier()
+{
+    static MainThreadNeverDestroyed<const AtomString> identifier("image-overlay-cropped-image", AtomString::ConstructFromLiteral);
+    return identifier;
+}
+
+static const AtomString& imageOverlayCroppedImageBackdropIdentifier()
+{
+    static MainThreadNeverDestroyed<const AtomString> identifier("image-overlay-cropped-image-backdrop", AtomString::ConstructFromLiteral);
+    return identifier;
+}
+
 bool hasOverlay(const HTMLElement& element)
 {
     auto shadowRoot = element.shadowRoot();
@@ -178,7 +195,7 @@
     if (!host)
         return false;
 
-    if (RefPtr overlay = static_cast<TreeScope&>(*host->userAgentShadowRoot()).getElementById(imageOverlayElementIdentifier()))
+    if (RefPtr overlay = host->userAgentShadowRoot()->getElementById(imageOverlayElementIdentifier()))
         return node.isDescendantOf(*overlay);
 
     return false;
@@ -198,7 +215,7 @@
         if (!shadowRoot)
             return;
 
-        if (RefPtr overlay = static_cast<TreeScope&>(*shadowRoot).getElementById(imageOverlayElementIdentifier()))
+        if (RefPtr overlay = shadowRoot->getElementById(imageOverlayElementIdentifier()))
             overlay->remove();
 
 #if ENABLE(IMAGE_ANALYSIS)
@@ -208,7 +225,13 @@
     });
 }
 
-#if ENABLE(IMAGE_ANALYSIS)
+static void installImageOverlayStyleSheet(ShadowRoot& shadowRoot)
+{
+    static MainThreadNeverDestroyed<const String> shadowStyle(StringImpl::createWithoutCopying(imageOverlayUserAgentStyleSheet, sizeof(imageOverlayUserAgentStyleSheet)));
+    auto style = HTMLStyleElement::create(HTMLNames::styleTag, shadowRoot.document(), false);
+    style->setTextContent(shadowStyle);
+    shadowRoot.appendChild(WTFMove(style));
+}
 
 IntRect containerRect(HTMLElement& element)
 {
@@ -222,6 +245,8 @@
     return enclosingIntRect(downcast<RenderImage>(*renderer).replacedContentRect());
 }
 
+#if ENABLE(IMAGE_ANALYSIS)
+
 struct LineElements {
     Ref<HTMLDivElement> line;
     Vector<Ref<HTMLElement>> children;
@@ -280,6 +305,7 @@
         }
     }
 
+    bool canUseExistingElements = false;
     if (elements.root) {
         for (auto& childElement : childrenOfType<HTMLDivElement>(*elements.root)) {
             if (!childElement.hasClass())
@@ -303,7 +329,7 @@
             elements.lines.append({ childElement, WTFMove(lineChildren), childrenOfType<HTMLBRElement>(childElement).first() });
         }
 
-        bool canUseExistingElements = ([&] {
+        canUseExistingElements = ([&] {
             if (result.dataDetectors.size() != elements.dataDetectors.size())
                 return false;
 
@@ -340,8 +366,8 @@
         })();
 
         if (!canUseExistingElements) {
-            elements.root->remove();
-            elements = { };
+            elements.root->removeChildren();
+            elements = { elements.root, { }, { }, { } };
         }
     }
 
@@ -350,23 +376,25 @@
 
     Ref document = element.document();
     Ref shadowRoot = element.ensureUserAgentShadowRoot();
-    if (!elements.root) {
-        auto rootContainer = HTMLDivElement::create(document.get());
-        rootContainer->setIdAttribute(imageOverlayElementIdentifier());
-        rootContainer->setTranslate(false);
-        if (document->isImageDocument())
-            rootContainer->setInlineStyleProperty(CSSPropertyWebkitUserSelect, CSSValueText);
+    if (!canUseExistingElements) {
+        if (!elements.root) {
+            auto rootContainer = HTMLDivElement::create(document.get());
+            rootContainer->setIdAttribute(imageOverlayElementIdentifier());
+            rootContainer->setTranslate(false);
+            if (document->isImageDocument())
+                rootContainer->setInlineStyleProperty(CSSPropertyWebkitUserSelect, CSSValueText);
 
-        if (mediaControlsContainer)
-            mediaControlsContainer->appendChild(rootContainer);
-        else
-            shadowRoot->appendChild(rootContainer);
-        elements.root = rootContainer.copyRef();
+            if (mediaControlsContainer)
+                mediaControlsContainer->appendChild(rootContainer);
+            else
+                shadowRoot->appendChild(rootContainer);
+            elements.root = rootContainer.copyRef();
+        }
         elements.lines.reserveInitialCapacity(result.lines.size());
         for (auto& line : result.lines) {
             auto lineContainer = HTMLDivElement::create(document.get());
             lineContainer->classList().add(imageOverlayLineClass());
-            rootContainer->appendChild(lineContainer);
+            elements.root->appendChild(lineContainer);
             LineElements lineElements { lineContainer, { }, { } };
             lineElements.children.reserveInitialCapacity(line.children.size());
             for (size_t childIndex = 0; childIndex < line.children.size(); ++childIndex) {
@@ -391,7 +419,7 @@
         for (auto& dataDetector : result.dataDetectors) {
             auto dataDetectorContainer = DataDetection::createElementForImageOverlay(document.get(), dataDetector);
             dataDetectorContainer->classList().add(imageOverlayDataDetectorClass());
-            rootContainer->appendChild(dataDetectorContainer);
+            elements.root->appendChild(dataDetectorContainer);
             elements.dataDetectors.uncheckedAppend(WTFMove(dataDetectorContainer));
         }
 #endif // ENABLE(DATA_DETECTION)
@@ -400,7 +428,7 @@
         for (auto& block : result.blocks) {
             auto blockContainer = HTMLDivElement::create(document.get());
             blockContainer->classList().add(imageOverlayBlockClass());
-            rootContainer->appendChild(blockContainer);
+            elements.root->appendChild(blockContainer);
             blockContainer->appendChild(Text::create(document.get(), makeString('\n', block.text)));
             elements.blocks.uncheckedAppend(WTFMove(blockContainer));
         }
@@ -411,12 +439,8 @@
         }
     }
 
-    if (!hadExistingElements) {
-        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));
-    }
+    if (!hadExistingElements)
+        installImageOverlayStyleSheet(shadowRoot.get());
 
     return elements;
 }
@@ -649,5 +673,87 @@
 
 #endif // ENABLE(IMAGE_ANALYSIS)
 
+std::unique_ptr<CroppedImage> CroppedImage::install(HTMLElement& host, Ref<SharedBuffer>&& imageData, const String& mimeType, FloatRect normalizedRect)
+{
+    Ref document = host.document();
+    Ref shadowRoot = host.ensureUserAgentShadowRoot();
+    RefPtr imageOverlayRoot = dynamicDowncast<HTMLDivElement>(shadowRoot->getElementById(imageOverlayElementIdentifier()));
+    if (!imageOverlayRoot) {
+        imageOverlayRoot = HTMLDivElement::create(document.get());
+        imageOverlayRoot->setIdAttribute(imageOverlayElementIdentifier());
+        imageOverlayRoot->setTranslate(false);
+        shadowRoot->appendChild(*imageOverlayRoot);
+        installImageOverlayStyleSheet(shadowRoot.get());
+    }
+
+    document->updateLayoutIgnorePendingStylesheets();
+
+    if (auto* renderer = dynamicDowncast<RenderImage>(host.renderer()))
+        renderer->setHasImageOverlay();
+
+    auto containerRect = ImageOverlay::containerRect(host);
+    auto cropRect = normalizedRect;
+    cropRect.scale(containerRect.width(), containerRect.height());
+    cropRect.move(containerRect.x(), containerRect.y());
+
+    auto croppedImageBackdrop = HTMLDivElement::create(document.get());
+    croppedImageBackdrop->setIdAttribute(imageOverlayCroppedImageBackdropIdentifier());
+    imageOverlayRoot->appendChild(croppedImageBackdrop.get());
+
+    auto croppedImage = HTMLImageElement::create(document.get());
+    auto croppedImageURL = DOMURL::createObjectURL(document.get(), Blob::create(document.ptr(), imageData->extractData(), mimeType));
+    croppedImage->setIdAttribute(imageOverlayCroppedImageIdentifier());
+    croppedImage->setAttributeWithoutSynchronization(HTMLNames::srcAttr, croppedImageURL);
+    croppedImage->setInlineStyleProperty(CSSPropertyLeft, cropRect.x(), CSSUnitType::CSS_PX);
+    croppedImage->setInlineStyleProperty(CSSPropertyTop, cropRect.y(), CSSUnitType::CSS_PX);
+    croppedImage->setInlineStyleProperty(CSSPropertyWidth, cropRect.width(), CSSUnitType::CSS_PX);
+    croppedImage->setInlineStyleProperty(CSSPropertyHeight, cropRect.height(), CSSUnitType::CSS_PX);
+    imageOverlayRoot->appendChild(croppedImage.get());
+
+    document->updateLayoutIgnorePendingStylesheets();
+    croppedImageBackdrop->setInlineStyleProperty(CSSPropertyOpacity, 0.5, CSSUnitType::CSS_NUMBER);
+
+    return makeUnique<CroppedImage>(document.get(), host, croppedImageBackdrop.get(), croppedImageURL);
+}
+
+CroppedImage::CroppedImage(Document& document, HTMLElement& host, HTMLElement& croppedImageBackdrop, const String& imageURL)
+    : m_document(document)
+    , m_host(host)
+    , m_croppedImageBackdrop(croppedImageBackdrop)
+    , m_imageURL(imageURL)
+{
+    setVisibility(true);
+}
+
+CroppedImage::~CroppedImage()
+{
+    if (RefPtr document = m_document.get())
+        DOMURL::revokeObjectURL(*document, m_imageURL);
+
+    RefPtr host = m_host.get();
+    if (!host)
+        return;
+
+    RefPtr shadowRoot = host->shadowRoot();
+    if (!shadowRoot || shadowRoot->mode() != ShadowRootMode::UserAgent)
+        return;
+
+    if (RefPtr croppedImage = shadowRoot->getElementById(imageOverlayCroppedImageIdentifier()))
+        croppedImage->remove();
+
+    if (RefPtr croppedImageBackdrop = shadowRoot->getElementById(imageOverlayCroppedImageBackdropIdentifier()))
+        croppedImageBackdrop->remove();
+}
+
+void CroppedImage::setVisibility(bool visible)
+{
+    RefPtr croppedImageBackdrop = m_croppedImageBackdrop.get();
+    if (!croppedImageBackdrop)
+        return;
+
+    croppedImageBackdrop->document().updateLayoutIgnorePendingStylesheets();
+    croppedImageBackdrop->setInlineStyleProperty(CSSPropertyOpacity, visible ? 0.5 : 0, CSSUnitType::CSS_NUMBER);
+}
+
 } // namespace ImageOverlay
 } // namespace WebCore

Modified: trunk/Source/WebCore/dom/ImageOverlay.h (290177 => 290178)


--- trunk/Source/WebCore/dom/ImageOverlay.h	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/dom/ImageOverlay.h	2022-02-18 23:50:27 UTC (rev 290178)
@@ -26,11 +26,15 @@
 #pragma once
 
 #include "IntRect.h"
+#include <wtf/RefCounted.h>
+#include <wtf/WeakPtr.h>
 
 namespace WebCore {
 
+class FloatRect;
 class HTMLElement;
 class Node;
+class SharedBuffer;
 class VisibleSelection;
 
 struct CharacterRange;
@@ -55,6 +59,24 @@
 WEBCORE_EXPORT void updateWithTextRecognitionResult(HTMLElement&, const TextRecognitionResult&, CacheTextRecognitionResults = CacheTextRecognitionResults::Yes);
 #endif
 
+class CroppedImage {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(CroppedImage);
+public:
+    WEBCORE_EXPORT static std::unique_ptr<CroppedImage> install(HTMLElement&, Ref<SharedBuffer>&&, const String&, FloatRect);
+
+    CroppedImage(Document&, HTMLElement&, HTMLElement&, const String& imageURL);
+    WEBCORE_EXPORT ~CroppedImage();
+
+    WEBCORE_EXPORT void setVisibility(bool);
+
+private:
+    WeakPtr<Document> m_document;
+    WeakPtr<HTMLElement> m_host;
+    WeakPtr<HTMLElement> m_croppedImageBackdrop;
+    String m_imageURL;
+};
+
 } // namespace ImageOverlay
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/dom/ShadowRoot.h (290177 => 290178)


--- trunk/Source/WebCore/dom/ShadowRoot.h	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/dom/ShadowRoot.h	2022-02-18 23:50:27 UTC (rev 290178)
@@ -61,6 +61,7 @@
 
     virtual ~ShadowRoot();
 
+    using TreeScope::getElementById;
     using TreeScope::rootNode;
 
     WEBCORE_EXPORT Style::Scope& styleScope();

Modified: trunk/Source/WebCore/html/shadow/imageOverlay.css (290177 => 290178)


--- trunk/Source/WebCore/html/shadow/imageOverlay.css	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/html/shadow/imageOverlay.css	2022-02-18 23:50:27 UTC (rev 290178)
@@ -71,3 +71,18 @@
     position: absolute;
     -webkit-user-select: none;
 }
+
+img#image-overlay-cropped-image {
+    position: absolute;
+    pointer-events: none;
+}
+
+div#image-overlay-cropped-image-backdrop {
+    position: absolute;
+    background: white;
+    width: 100%;
+    height: 100%;
+    transition: opacity 0.5s ease-in-out;
+    opacity: 0;
+    pointer-events: none;
+}

Modified: trunk/Source/WebCore/testing/Internals.cpp (290177 => 290178)


--- trunk/Source/WebCore/testing/Internals.cpp	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/testing/Internals.cpp	2022-02-18 23:50:27 UTC (rev 290178)
@@ -5134,8 +5134,13 @@
 }
 #endif
 
-#endif
+RefPtr<SharedBuffer> Internals::pngDataForTesting()
+{
+    return nullptr;
+}
 
+#endif // !PLATFORM(COCOA)
+
 #if ENABLE(VIDEO)
 bool Internals::isMediaElementHidden(const HTMLMediaElement& media)
 {
@@ -5879,6 +5884,29 @@
 #endif
 }
 
+void Internals::installCroppedImageOverlay(Element& element, Ref<DOMRectReadOnly>&& normalizedCropRect)
+{
+    RefPtr htmlElement = dynamicDowncast<HTMLElement>(element);
+    if (!htmlElement)
+        return;
+
+    auto imageData = pngDataForTesting();
+    if (!imageData)
+        return;
+
+    m_croppedImageOverlay = ImageOverlay::CroppedImage::install(*htmlElement, imageData.releaseNonNull(), "image/png"_s, {
+        static_cast<float>(normalizedCropRect->x()),
+        static_cast<float>(normalizedCropRect->y()),
+        static_cast<float>(normalizedCropRect->width()),
+        static_cast<float>(normalizedCropRect->height())
+    });
+}
+
+void Internals::uninstallCroppedImageOverlay()
+{
+    m_croppedImageOverlay = nullptr;
+}
+
 bool Internals::hasActiveDataDetectorHighlight() const
 {
 #if ENABLE(DATA_DETECTION) && ENABLE(IMAGE_ANALYSIS)

Modified: trunk/Source/WebCore/testing/Internals.h (290177 => 290178)


--- trunk/Source/WebCore/testing/Internals.h	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/testing/Internals.h	2022-02-18 23:50:27 UTC (rev 290178)
@@ -106,6 +106,7 @@
 class SVGSVGElement;
 class ScrollableArea;
 class SerializedScriptValue;
+class SharedBuffer;
 class SourceBuffer;
 class StringCallback;
 class StyleSheet;
@@ -151,6 +152,10 @@
 #endif
 #endif
 
+namespace ImageOverlay {
+class CroppedImage;
+}
+
 template<typename IDLType> class DOMPromiseDeferred;
 
 struct MockWebAuthenticationConfiguration;
@@ -950,6 +955,8 @@
     };
 
     void installImageOverlay(Element&, Vector<ImageOverlayLine>&&, Vector<ImageOverlayBlock>&& = { }, Vector<ImageOverlayDataDetector>&& = { });
+    void installCroppedImageOverlay(Element&, Ref<DOMRectReadOnly>&&);
+    void uninstallCroppedImageOverlay();
     bool hasActiveDataDetectorHighlight() const;
 
 #if ENABLE(IMAGE_ANALYSIS)
@@ -1275,6 +1282,8 @@
     static DDScannerResult *fakeDataDetectorResultForTesting();
 #endif
 
+    static RefPtr<SharedBuffer> pngDataForTesting();
+
 #if ENABLE(MEDIA_STREAM)
     // RealtimeMediaSource::Observer API
     void videoSampleAvailable(MediaSample&, VideoSampleMetadata) final;
@@ -1308,6 +1317,8 @@
 #if ENABLE(VIDEO)
     std::unique_ptr<CaptionUserPreferencesTestingModeToken> m_testingModeToken;
 #endif
+
+    std::unique_ptr<ImageOverlay::CroppedImage> m_croppedImageOverlay;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/testing/Internals.idl (290177 => 290178)


--- trunk/Source/WebCore/testing/Internals.idl	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/testing/Internals.idl	2022-02-18 23:50:27 UTC (rev 290178)
@@ -978,6 +978,8 @@
     [Conditional=IMAGE_ANALYSIS] readonly attribute Element? textRecognitionCandidate;
     [Conditional=IMAGE_ANALYSIS] undefined requestTextRecognition(Element element, VoidCallback callback);
     undefined installImageOverlay(Element element, sequence<ImageOverlayLine> lines, optional sequence<ImageOverlayBlock> blocks = [], optional sequence<ImageOverlayDataDetector> dataDetectors = []);
+    undefined installCroppedImageOverlay(Element element, DOMRectReadOnly normalizedCropRect);
+    undefined uninstallCroppedImageOverlay();
     readonly attribute boolean hasActiveDataDetectorHighlight;
 
     boolean usingAppleInternalSDK();

Modified: trunk/Source/WebCore/testing/Internals.mm (290177 => 290178)


--- trunk/Source/WebCore/testing/Internals.mm	2022-02-18 23:42:31 UTC (rev 290177)
+++ trunk/Source/WebCore/testing/Internals.mm	2022-02-18 23:50:27 UTC (rev 290178)
@@ -35,6 +35,7 @@
 #import "HitTestResult.h"
 #import "MediaPlayerPrivate.h"
 #import "Range.h"
+#import "SharedBuffer.h"
 #import "SimpleRange.h"
 #import "UTIUtilities.h"
 #import <AVFoundation/AVPlayer.h>
@@ -197,4 +198,10 @@
 
 #endif // ENABLE(DATA_DETECTION)
 
+RefPtr<SharedBuffer> Internals::pngDataForTesting()
+{
+    NSBundle *webCoreBundle = [NSBundle bundleForClass:NSClassFromString(@"WebCoreBundleFinder")];
+    return SharedBuffer::createWithContentsOfFile([webCoreBundle pathForResource:@"missingImage" ofType:@"png"]);
 }
+
+} // namespace WebCore
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to