Title: [277747] trunk/Source/WebCore
Revision
277747
Author
[email protected]
Date
2021-05-19 13:30:27 -0700 (Wed, 19 May 2021)

Log Message

[macOS] Pull ServicesOverlayController::Highlight out into a separate helper class
https://bugs.webkit.org/show_bug.cgi?id=225968

Reviewed by Tim Horton.

Refactor some code for painting data detector highlights (`DDHighlightRef`), by pulling what is currently
`ServicesOverlayController::Highlight` out into a separate helper class. In a future patch, I plan on using this
functionality for the image overlay controller to support rendering data detector highlights there.

This patch also makes some minor adjustments:

-   Make `DataDetectorHighlight` capable of being stored in a `WeakPtr`, and have `ServicesOverlayController`
    hold on to a `WeakHashSet<DataDetectorHighlight>` instead of a hash set of raw pointers. This additionally
    makes it unnecessary to add methods for `DataDetectorHighlight` to notify `ServicesOverlayController` upon
    creation or destruction, since we can simply add the newly created highlight to the weak set after creating
    it, and since it's a `WeakPtr`, it will get cleaned up upon destruction.

-   Make DataDetectorHighlight::Type an enum class, and make `m_dirtyHighlightTypes` an `OptionSet` instead of
    a raw bitmask.

-   Use `auto` in a few places where the type is apparent (e.g. in some places where we use `adoptCF`).

-   Make `highlightFadeAnimationDuration` a `WTF::Seconds` instead of a raw floating point value.

-   Add a FIXME in `DataDetectorHighlight::paintContents` about the need to refactor this painting logic so that
    it does *not* use the platform `CGContextRef` directly.

No change in behavior.

* SourcesCocoa.txt:
* WebCore.xcodeproj/project.pbxproj:
* page/mac/ServicesOverlayController.h:
(WebCore::ServicesOverlayController::activeHighlight const):
(WebCore::ServicesOverlayController::Highlight::ddHighlight const): Deleted.
(WebCore::ServicesOverlayController::Highlight::range const): Deleted.
(WebCore::ServicesOverlayController::Highlight::layer const): Deleted.
(WebCore::ServicesOverlayController::Highlight::type const): Deleted.
* page/mac/ServicesOverlayController.mm:
(WebCore::ServicesOverlayController::~ServicesOverlayController):
(WebCore::ServicesOverlayController::selectionRectsDidChange):
(WebCore::ServicesOverlayController::selectedTelephoneNumberRangesChanged):
(WebCore::ServicesOverlayController::invalidateHighlightsOfType):
(WebCore::ServicesOverlayController::buildPotentialHighlightsIfNeeded):
(WebCore::ServicesOverlayController::mouseIsOverHighlight const):
(WebCore::ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown const):
(WebCore::ServicesOverlayController::removeAllPotentialHighlightsOfType):
(WebCore::ServicesOverlayController::buildPhoneNumberHighlights):
(WebCore::ServicesOverlayController::buildSelectionHighlight):
(WebCore::ServicesOverlayController::replaceHighlightsOfTypePreservingEquivalentHighlights):
(WebCore::ServicesOverlayController::findTelephoneNumberHighlightContainingSelectionHighlight):
(WebCore::ServicesOverlayController::determineActiveHighlight):
(WebCore::ServicesOverlayController::mouseEvent):
(WebCore::ServicesOverlayController::didScrollFrame):
(WebCore::ServicesOverlayController::handleClick):
(WebCore::ServicesOverlayController::shouldRemoveHighlightLayerAfterFadingOut const):
(WebCore::ServicesOverlayController::Highlight::createForSelection): Deleted.
(WebCore::ServicesOverlayController::Highlight::createForTelephoneNumber): Deleted.
(WebCore::ServicesOverlayController::Highlight::Highlight): Deleted.
(WebCore::ServicesOverlayController::Highlight::~Highlight): Deleted.
(WebCore::ServicesOverlayController::Highlight::setDDHighlight): Deleted.
(WebCore::ServicesOverlayController::Highlight::invalidate): Deleted.
(WebCore::ServicesOverlayController::Highlight::notifyFlushRequired): Deleted.
(WebCore::ServicesOverlayController::Highlight::paintContents): Deleted.
(WebCore::ServicesOverlayController::Highlight::deviceScaleFactor const): Deleted.
(WebCore::ServicesOverlayController::Highlight::fadeIn): Deleted.
(WebCore::ServicesOverlayController::Highlight::fadeOut): Deleted.
(WebCore::ServicesOverlayController::Highlight::didFinishFadeOutAnimation): Deleted.
(WebCore::ServicesOverlayController::highlightsAreEquivalent): Deleted.
(WebCore::ServicesOverlayController::didCreateHighlight): Deleted.
(WebCore::ServicesOverlayController::willDestroyHighlight): Deleted.
* platform/mac/DataDetectorHighlight.h: Added.
(WebCore::DataDetectorHighlightClient::shouldRemoveHighlightLayerAfterFadingOut const):
(WebCore::DataDetectorHighlight::highlight const):
(WebCore::DataDetectorHighlight::range const):
(WebCore::DataDetectorHighlight::layer const):
(WebCore::DataDetectorHighlight::type const):
* platform/mac/DataDetectorHighlight.mm: Added.
(WebCore::DataDetectorHighlight::createForSelection):
(WebCore::DataDetectorHighlight::createForTelephoneNumber):
(WebCore::DataDetectorHighlight::DataDetectorHighlight):
(WebCore::DataDetectorHighlight::setHighlight):
(WebCore::DataDetectorHighlight::invalidate):
(WebCore::DataDetectorHighlight::notifyFlushRequired):
(WebCore::DataDetectorHighlight::paintContents):
(WebCore::DataDetectorHighlight::deviceScaleFactor const):
(WebCore::DataDetectorHighlight::fadeIn):
(WebCore::DataDetectorHighlight::fadeOut):
(WebCore::DataDetectorHighlight::didFinishFadeOutAnimation):
(WebCore::areEquivalent):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (277746 => 277747)


--- trunk/Source/WebCore/ChangeLog	2021-05-19 19:53:43 UTC (rev 277746)
+++ trunk/Source/WebCore/ChangeLog	2021-05-19 20:30:27 UTC (rev 277747)
@@ -1,3 +1,95 @@
+2021-05-19  Wenson Hsieh  <[email protected]>
+
+        [macOS] Pull ServicesOverlayController::Highlight out into a separate helper class
+        https://bugs.webkit.org/show_bug.cgi?id=225968
+
+        Reviewed by Tim Horton.
+
+        Refactor some code for painting data detector highlights (`DDHighlightRef`), by pulling what is currently
+        `ServicesOverlayController::Highlight` out into a separate helper class. In a future patch, I plan on using this
+        functionality for the image overlay controller to support rendering data detector highlights there.
+
+        This patch also makes some minor adjustments:
+
+        -   Make `DataDetectorHighlight` capable of being stored in a `WeakPtr`, and have `ServicesOverlayController`
+            hold on to a `WeakHashSet<DataDetectorHighlight>` instead of a hash set of raw pointers. This additionally
+            makes it unnecessary to add methods for `DataDetectorHighlight` to notify `ServicesOverlayController` upon
+            creation or destruction, since we can simply add the newly created highlight to the weak set after creating
+            it, and since it's a `WeakPtr`, it will get cleaned up upon destruction.
+
+        -   Make DataDetectorHighlight::Type an enum class, and make `m_dirtyHighlightTypes` an `OptionSet` instead of
+            a raw bitmask.
+
+        -   Use `auto` in a few places where the type is apparent (e.g. in some places where we use `adoptCF`).
+
+        -   Make `highlightFadeAnimationDuration` a `WTF::Seconds` instead of a raw floating point value.
+
+        -   Add a FIXME in `DataDetectorHighlight::paintContents` about the need to refactor this painting logic so that
+            it does *not* use the platform `CGContextRef` directly.
+
+        No change in behavior.
+
+        * SourcesCocoa.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * page/mac/ServicesOverlayController.h:
+        (WebCore::ServicesOverlayController::activeHighlight const):
+        (WebCore::ServicesOverlayController::Highlight::ddHighlight const): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::range const): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::layer const): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::type const): Deleted.
+        * page/mac/ServicesOverlayController.mm:
+        (WebCore::ServicesOverlayController::~ServicesOverlayController):
+        (WebCore::ServicesOverlayController::selectionRectsDidChange):
+        (WebCore::ServicesOverlayController::selectedTelephoneNumberRangesChanged):
+        (WebCore::ServicesOverlayController::invalidateHighlightsOfType):
+        (WebCore::ServicesOverlayController::buildPotentialHighlightsIfNeeded):
+        (WebCore::ServicesOverlayController::mouseIsOverHighlight const):
+        (WebCore::ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown const):
+        (WebCore::ServicesOverlayController::removeAllPotentialHighlightsOfType):
+        (WebCore::ServicesOverlayController::buildPhoneNumberHighlights):
+        (WebCore::ServicesOverlayController::buildSelectionHighlight):
+        (WebCore::ServicesOverlayController::replaceHighlightsOfTypePreservingEquivalentHighlights):
+        (WebCore::ServicesOverlayController::findTelephoneNumberHighlightContainingSelectionHighlight):
+        (WebCore::ServicesOverlayController::determineActiveHighlight):
+        (WebCore::ServicesOverlayController::mouseEvent):
+        (WebCore::ServicesOverlayController::didScrollFrame):
+        (WebCore::ServicesOverlayController::handleClick):
+        (WebCore::ServicesOverlayController::shouldRemoveHighlightLayerAfterFadingOut const):
+        (WebCore::ServicesOverlayController::Highlight::createForSelection): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::createForTelephoneNumber): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::Highlight): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::~Highlight): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::setDDHighlight): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::invalidate): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::notifyFlushRequired): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::paintContents): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::deviceScaleFactor const): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::fadeIn): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::fadeOut): Deleted.
+        (WebCore::ServicesOverlayController::Highlight::didFinishFadeOutAnimation): Deleted.
+        (WebCore::ServicesOverlayController::highlightsAreEquivalent): Deleted.
+        (WebCore::ServicesOverlayController::didCreateHighlight): Deleted.
+        (WebCore::ServicesOverlayController::willDestroyHighlight): Deleted.
+        * platform/mac/DataDetectorHighlight.h: Added.
+        (WebCore::DataDetectorHighlightClient::shouldRemoveHighlightLayerAfterFadingOut const):
+        (WebCore::DataDetectorHighlight::highlight const):
+        (WebCore::DataDetectorHighlight::range const):
+        (WebCore::DataDetectorHighlight::layer const):
+        (WebCore::DataDetectorHighlight::type const):
+        * platform/mac/DataDetectorHighlight.mm: Added.
+        (WebCore::DataDetectorHighlight::createForSelection):
+        (WebCore::DataDetectorHighlight::createForTelephoneNumber):
+        (WebCore::DataDetectorHighlight::DataDetectorHighlight):
+        (WebCore::DataDetectorHighlight::setHighlight):
+        (WebCore::DataDetectorHighlight::invalidate):
+        (WebCore::DataDetectorHighlight::notifyFlushRequired):
+        (WebCore::DataDetectorHighlight::paintContents):
+        (WebCore::DataDetectorHighlight::deviceScaleFactor const):
+        (WebCore::DataDetectorHighlight::fadeIn):
+        (WebCore::DataDetectorHighlight::fadeOut):
+        (WebCore::DataDetectorHighlight::didFinishFadeOutAnimation):
+        (WebCore::areEquivalent):
+
 2021-05-18  Darin Adler  <[email protected]>
 
         Move CFStringRef and NSString support from StringBuilder into StringConcatenateCF

Modified: trunk/Source/WebCore/SourcesCocoa.txt (277746 => 277747)


--- trunk/Source/WebCore/SourcesCocoa.txt	2021-05-19 19:53:43 UTC (rev 277746)
+++ trunk/Source/WebCore/SourcesCocoa.txt	2021-05-19 20:30:27 UTC (rev 277747)
@@ -481,6 +481,7 @@
 platform/ios/wak/WebCoreThreadSystemInterface.cpp
 platform/mac/BlocklistUpdater.mm
 platform/mac/CursorMac.mm @no-unify
+platform/mac/DataDetectorHighlight.mm
 platform/mac/HIDDevice.cpp
 platform/mac/HIDElement.cpp
 platform/mac/KeyEventMac.mm @no-unify

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (277746 => 277747)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-05-19 19:53:43 UTC (rev 277746)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-05-19 20:30:27 UTC (rev 277747)
@@ -5355,6 +5355,7 @@
 		F48D2A7E2157182600C6752B /* FontAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = F48D2A712156DC0A00C6752B /* FontAttributes.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		F48D2AA52159740D00C6752B /* ColorCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = F48D2AA32159740D00C6752B /* ColorCocoa.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		F49786881FF45FA500E060AB /* PasteboardItemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F49786871FF45FA500E060AB /* PasteboardItemInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		F4B2A909265030BA009E7286 /* DataDetectorHighlight.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B2A90626502BA0009E7286 /* DataDetectorHighlight.h */; };
 		F4B422C4220C0568009E1E7D /* DOMPasteAccess.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B422C2220C0000009E1E7D /* DOMPasteAccess.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		F4BFB9851E1DDF9B00862C24 /* DumpEditingHistory.js in Copy Scripts */ = {isa = PBXBuildFile; fileRef = F48389831E1DDF2B0076B7EA /* DumpEditingHistory.js */; };
 		F4BFB9861E1DDF9B00862C24 /* EditingHistoryUtil.js in Copy Scripts */ = {isa = PBXBuildFile; fileRef = F48389841E1DDF2B0076B7EA /* EditingHistoryUtil.js */; };
@@ -16833,6 +16834,8 @@
 		F48D2AA42159740D00C6752B /* ColorCocoa.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ColorCocoa.mm; sourceTree = "<group>"; };
 		F49786871FF45FA500E060AB /* PasteboardItemInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PasteboardItemInfo.h; sourceTree = "<group>"; };
 		F49E98E421DEE6C1009AE55E /* EditAction.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EditAction.cpp; sourceTree = "<group>"; };
+		F4B2A90626502BA0009E7286 /* DataDetectorHighlight.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataDetectorHighlight.h; sourceTree = "<group>"; };
+		F4B2A90826502BC0009E7286 /* DataDetectorHighlight.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DataDetectorHighlight.mm; sourceTree = "<group>"; };
 		F4B422C2220C0000009E1E7D /* DOMPasteAccess.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DOMPasteAccess.h; sourceTree = "<group>"; };
 		F4C5F2052479E7EE00E9B4F5 /* ClipboardImageReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClipboardImageReader.h; sourceTree = "<group>"; };
 		F4C5F2082479E9C800E9B4F5 /* ClipboardImageReader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ClipboardImageReader.cpp; sourceTree = "<group>"; };
@@ -22175,6 +22178,8 @@
 				1AFFC44D1D5E7EC700267A66 /* BlocklistUpdater.h */,
 				1AFFC44E1D5E7EC700267A66 /* BlocklistUpdater.mm */,
 				F58784F002DE375901EA4122 /* CursorMac.mm */,
+				F4B2A90626502BA0009E7286 /* DataDetectorHighlight.h */,
+				F4B2A90826502BC0009E7286 /* DataDetectorHighlight.mm */,
 				E1BA66F01742BD8600C20251 /* DynamicLinkerInterposing.h */,
 				510A91E524CFCC1800BFD89C /* HIDDevice.cpp */,
 				510A91E224CFCC1800BFD89C /* HIDDevice.h */,
@@ -32168,6 +32173,7 @@
 				97BC6A321505F081001B74AC /* DatabaseTracker.h in Headers */,
 				BE23480D18A9871400E4B6E8 /* DataCue.h in Headers */,
 				C5227DF11C3C6DF100F5ED54 /* DataDetection.h in Headers */,
+				F4B2A909265030BA009E7286 /* DataDetectorHighlight.h in Headers */,
 				BC4A533525605A560028C592 /* DataDetectorType.h in Headers */,
 				E58B45BA20AD07DD00991025 /* DataListButtonElement.h in Headers */,
 				E517670320B88C1400D41167 /* DataListSuggestionInformation.h in Headers */,

Modified: trunk/Source/WebCore/page/mac/ServicesOverlayController.h (277746 => 277747)


--- trunk/Source/WebCore/page/mac/ServicesOverlayController.h	2021-05-19 19:53:43 UTC (rev 277746)
+++ trunk/Source/WebCore/page/mac/ServicesOverlayController.h	2021-05-19 20:30:27 UTC (rev 277747)
@@ -27,14 +27,14 @@
 
 #if (ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION)) && PLATFORM(MAC)
 
+#include "DataDetectorHighlight.h"
 #include "GraphicsLayerClient.h"
 #include "PageOverlay.h"
-#include "Range.h"
 #include "Timer.h"
 #include <wtf/MonotonicTime.h>
+#include <wtf/OptionSet.h>
+#include <wtf/WeakHashSet.h>
 
-typedef struct __DDHighlight *DDHighlightRef;
-
 namespace WebCore {
     
 class LayoutRect;
@@ -42,7 +42,7 @@
 
 struct GapRects;
 
-class ServicesOverlayController : private PageOverlay::Client {
+class ServicesOverlayController : private DataDetectorHighlightClient, private PageOverlay::Client {
     WTF_MAKE_FAST_ALLOCATED;
 public:
     explicit ServicesOverlayController(Page&);
@@ -52,48 +52,6 @@
     void selectionRectsDidChange(const Vector<LayoutRect>&, const Vector<GapRects>&, bool isTextOnly);
 
 private:
-    class Highlight : public RefCounted<Highlight>, private GraphicsLayerClient {
-        WTF_MAKE_NONCOPYABLE(Highlight);
-    public:
-        static Ref<Highlight> createForSelection(ServicesOverlayController&, RetainPtr<DDHighlightRef>&&, SimpleRange&&);
-        static Ref<Highlight> createForTelephoneNumber(ServicesOverlayController&, RetainPtr<DDHighlightRef>&&, SimpleRange&&);
-        ~Highlight();
-
-        void invalidate();
-
-        DDHighlightRef ddHighlight() const { return m_ddHighlight.get(); }
-        const SimpleRange& range() const { return m_range; }
-        GraphicsLayer& layer() const { return m_graphicsLayer.get(); }
-
-        enum {
-            TelephoneNumberType = 1 << 0,
-            SelectionType = 1 << 1,
-        };
-        typedef uint8_t Type;
-        Type type() const { return m_type; }
-
-        void fadeIn();
-        void fadeOut();
-
-        void setDDHighlight(DDHighlightRef);
-
-    private:
-        Highlight(ServicesOverlayController&, Type, RetainPtr<DDHighlightRef>&&, SimpleRange&&);
-
-        // GraphicsLayerClient
-        void notifyFlushRequired(const GraphicsLayer*) override;
-        void paintContents(const GraphicsLayer*, GraphicsContext&, const FloatRect& inClip, GraphicsLayerPaintBehavior) override;
-        float deviceScaleFactor() const override;
-
-        void didFinishFadeOutAnimation();
-
-        ServicesOverlayController* m_controller;
-        RetainPtr<DDHighlightRef> m_ddHighlight;
-        SimpleRange m_range;
-        Ref<GraphicsLayer> m_graphicsLayer;
-        Type m_type;
-    };
-
     // PageOverlay::Client
     void willMoveToPage(PageOverlay&, Page*) override;
     void didMoveToPage(PageOverlay&, Page*) override;
@@ -102,38 +60,32 @@
     void didScrollFrame(PageOverlay&, Frame&) override;
 
     void createOverlayIfNeeded();
-    void handleClick(const IntPoint&, Highlight&);
+    void handleClick(const IntPoint&, DataDetectorHighlight&);
 
-    void drawHighlight(Highlight&, GraphicsContext&);
+    void drawHighlight(DataDetectorHighlight&, GraphicsContext&);
 
-    void invalidateHighlightsOfType(Highlight::Type);
+    void invalidateHighlightsOfType(DataDetectorHighlight::Type);
     void buildPotentialHighlightsIfNeeded();
 
-    void replaceHighlightsOfTypePreservingEquivalentHighlights(HashSet<RefPtr<Highlight>>&, Highlight::Type);
-    void removeAllPotentialHighlightsOfType(Highlight::Type);
+    void replaceHighlightsOfTypePreservingEquivalentHighlights(HashSet<RefPtr<DataDetectorHighlight>>&, DataDetectorHighlight::Type);
+    void removeAllPotentialHighlightsOfType(DataDetectorHighlight::Type);
     void buildPhoneNumberHighlights();
     void buildSelectionHighlight();
 
     void determineActiveHighlight(bool& mouseIsOverButton);
     void clearActiveHighlight();
-    Highlight* activeHighlight() const { return m_activeHighlight.get(); }
+    DataDetectorHighlight* activeHighlight() const final { return m_activeHighlight.get(); }
 
-    Highlight* findTelephoneNumberHighlightContainingSelectionHighlight(Highlight&);
+    DataDetectorHighlight* findTelephoneNumberHighlightContainingSelectionHighlight(DataDetectorHighlight&);
 
     bool hasRelevantSelectionServices();
 
-    bool mouseIsOverHighlight(Highlight&, bool& mouseIsOverButton) const;
-    Seconds remainingTimeUntilHighlightShouldBeShown(Highlight*) const;
+    bool mouseIsOverHighlight(DataDetectorHighlight&, bool& mouseIsOverButton) const;
+    Seconds remainingTimeUntilHighlightShouldBeShown(DataDetectorHighlight*) const;
     void determineActiveHighlightTimerFired();
 
-    static bool highlightsAreEquivalent(const Highlight*, const Highlight*);
-
     Vector<SimpleRange> telephoneNumberRangesForFocusedFrame();
 
-    void didCreateHighlight(Highlight*);
-    void willDestroyHighlight(Highlight*);
-    void didFinishFadingOutHighlight(Highlight*);
-
     Frame& mainFrame() const;
     Page& page() const { return m_page; }
 
@@ -140,24 +92,22 @@
     Page& m_page;
     PageOverlay* m_servicesOverlay { nullptr };
 
-    RefPtr<Highlight> m_activeHighlight;
-    RefPtr<Highlight> m_nextActiveHighlight;
-    HashSet<RefPtr<Highlight>> m_potentialHighlights;
-    HashSet<RefPtr<Highlight>> m_animatingHighlights;
+    RefPtr<DataDetectorHighlight> m_activeHighlight;
+    RefPtr<DataDetectorHighlight> m_nextActiveHighlight;
+    HashSet<RefPtr<DataDetectorHighlight>> m_potentialHighlights;
+    HashSet<RefPtr<DataDetectorHighlight>> m_animatingHighlights;
+    WeakHashSet<DataDetectorHighlight> m_highlights;
 
-    HashSet<Highlight*> m_highlights;
-
-    // FIXME: These should move onto Highlight.
     Vector<LayoutRect> m_currentSelectionRects;
     bool m_isTextOnly { false };
 
-    Highlight::Type m_dirtyHighlightTypes { 0 };
+    OptionSet<DataDetectorHighlight::Type> m_dirtyHighlightTypes;
 
     MonotonicTime m_lastSelectionChangeTime;
     MonotonicTime m_nextActiveHighlightChangeTime;
     MonotonicTime m_lastMouseUpTime;
 
-    RefPtr<Highlight> m_currentMouseDownOnButtonHighlight;
+    RefPtr<DataDetectorHighlight> m_currentMouseDownOnButtonHighlight;
     IntPoint m_mousePosition;
 
     Timer m_determineActiveHighlightTimer;

Modified: trunk/Source/WebCore/page/mac/ServicesOverlayController.mm (277746 => 277747)


--- trunk/Source/WebCore/page/mac/ServicesOverlayController.mm	2021-05-19 19:53:43 UTC (rev 277746)
+++ trunk/Source/WebCore/page/mac/ServicesOverlayController.mm	2021-05-19 20:30:27 UTC (rev 277747)
@@ -45,151 +45,13 @@
 #import "Logging.h"
 #import "Page.h"
 #import "PageOverlayController.h"
-#import "PlatformCAAnimationCocoa.h"
 #import "Settings.h"
 #import <QuartzCore/QuartzCore.h>
 #import <pal/spi/mac/DataDetectorsSPI.h>
 #import <wtf/SoftLinking.h>
 
-const float highlightFadeAnimationDuration = 0.3;
-
 namespace WebCore {
 
-Ref<ServicesOverlayController::Highlight> ServicesOverlayController::Highlight::createForSelection(ServicesOverlayController& controller, RetainPtr<DDHighlightRef>&& ddHighlight, SimpleRange&& range)
-{
-    return adoptRef(*new Highlight(controller, Highlight::SelectionType, WTFMove(ddHighlight), WTFMove(range)));
-}
-
-Ref<ServicesOverlayController::Highlight> ServicesOverlayController::Highlight::createForTelephoneNumber(ServicesOverlayController& controller, RetainPtr<DDHighlightRef>&& ddHighlight, SimpleRange&& range)
-{
-    return adoptRef(*new Highlight(controller, Highlight::TelephoneNumberType, WTFMove(ddHighlight), WTFMove(range)));
-}
-
-ServicesOverlayController::Highlight::Highlight(ServicesOverlayController& controller, Type type, RetainPtr<DDHighlightRef>&& ddHighlight, SimpleRange&& range)
-    : m_controller(&controller)
-    , m_range(WTFMove(range))
-    , m_graphicsLayer(GraphicsLayer::create(controller.page().chrome().client().graphicsLayerFactory(), *this))
-    , m_type(type)
-{
-    ASSERT(ddHighlight);
-
-    m_graphicsLayer->setDrawsContent(true);
-
-    setDDHighlight(ddHighlight.get());
-
-    // Set directly on the PlatformCALayer so that when we leave the 'from' value implicit
-    // in our animations, we get the right initial value regardless of flush timing.
-    downcast<GraphicsLayerCA>(layer()).platformCALayer()->setOpacity(0);
-
-    controller.didCreateHighlight(this);
-}
-
-ServicesOverlayController::Highlight::~Highlight()
-{
-    if (m_controller)
-        m_controller->willDestroyHighlight(this);
-}
-
-void ServicesOverlayController::Highlight::setDDHighlight(DDHighlightRef highlight)
-{
-    if (!DataDetectorsLibrary())
-        return;
-
-    if (!m_controller)
-        return;
-
-    m_ddHighlight = highlight;
-
-    if (!m_ddHighlight)
-        return;
-
-    CGRect highlightBoundingRect = DDHighlightGetBoundingRect(m_ddHighlight.get());
-    m_graphicsLayer->setPosition(FloatPoint(highlightBoundingRect.origin));
-    m_graphicsLayer->setSize(FloatSize(highlightBoundingRect.size));
-
-    m_graphicsLayer->setNeedsDisplay();
-}
-
-void ServicesOverlayController::Highlight::invalidate()
-{
-    layer().removeFromParent();
-    m_controller = nullptr;
-}
-
-void ServicesOverlayController::Highlight::notifyFlushRequired(const GraphicsLayer*)
-{
-    if (!m_controller)
-        return;
-
-    m_controller->page().scheduleRenderingUpdate(RenderingUpdateStep::LayerFlush);
-}
-
-void ServicesOverlayController::Highlight::paintContents(const GraphicsLayer*, GraphicsContext& graphicsContext, const FloatRect&, GraphicsLayerPaintBehavior)
-{
-    if (!DataDetectorsLibrary())
-        return;
-
-    CGContextRef cgContext = graphicsContext.platformContext();
-
-    ALLOW_DEPRECATED_DECLARATIONS_BEGIN
-    CGLayerRef highlightLayer = DDHighlightGetLayerWithContext(ddHighlight(), cgContext);
-    ALLOW_DEPRECATED_DECLARATIONS_END
-    CGRect highlightBoundingRect = DDHighlightGetBoundingRect(ddHighlight());
-    highlightBoundingRect.origin = CGPointZero;
-
-    CGContextDrawLayerInRect(cgContext, highlightBoundingRect, highlightLayer);
-}
-
-float ServicesOverlayController::Highlight::deviceScaleFactor() const
-{
-    if (!m_controller)
-        return 1;
-
-    return m_controller->page().deviceScaleFactor();
-}
-
-void ServicesOverlayController::Highlight::fadeIn()
-{
-    RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
-    [animation setDuration:highlightFadeAnimationDuration];
-    [animation setFillMode:kCAFillModeForwards];
-    [animation setRemovedOnCompletion:false];
-    [animation setToValue:@1];
-
-    auto platformAnimation = PlatformCAAnimationCocoa::create(animation.get());
-    downcast<GraphicsLayerCA>(layer()).platformCALayer()->addAnimationForKey("FadeHighlightIn", platformAnimation.get());
-}
-
-void ServicesOverlayController::Highlight::fadeOut()
-{
-    RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
-    [animation setDuration:highlightFadeAnimationDuration];
-    [animation setFillMode:kCAFillModeForwards];
-    [animation setRemovedOnCompletion:false];
-    [animation setToValue:@0];
-
-    RefPtr<Highlight> retainedSelf = this;
-    [CATransaction begin];
-    [CATransaction setCompletionBlock:[retainedSelf] () {
-        retainedSelf->didFinishFadeOutAnimation();
-    }];
-
-    auto platformAnimation = PlatformCAAnimationCocoa::create(animation.get());
-    downcast<GraphicsLayerCA>(layer()).platformCALayer()->addAnimationForKey("FadeHighlightOut", platformAnimation.get());
-    [CATransaction commit];
-}
-
-void ServicesOverlayController::Highlight::didFinishFadeOutAnimation()
-{
-    if (!m_controller)
-        return;
-
-    if (m_controller->activeHighlight() == this)
-        return;
-
-    layer().removeFromParent();
-}
-
 ServicesOverlayController::ServicesOverlayController(Page& page)
     : m_page(page)
     , m_determineActiveHighlightTimer(*this, &ServicesOverlayController::determineActiveHighlightTimerFired)
@@ -200,7 +62,7 @@
 ServicesOverlayController::~ServicesOverlayController()
 {
     for (auto& highlight : m_highlights)
-        highlight->invalidate();
+        highlight.invalidate();
 }
 
 void ServicesOverlayController::willMoveToPage(PageOverlay&, Page* page)
@@ -358,36 +220,36 @@
     m_currentSelectionRects.reverse();
 
     LOG(Services, "ServicesOverlayController - Selection rects changed - Now have %lu\n", rects.size());
-    invalidateHighlightsOfType(Highlight::SelectionType);
+    invalidateHighlightsOfType(DataDetectorHighlight::Type::Selection);
 }
 
 void ServicesOverlayController::selectedTelephoneNumberRangesChanged()
 {
     LOG(Services, "ServicesOverlayController - Telephone number ranges changed\n");
-    invalidateHighlightsOfType(Highlight::TelephoneNumberType);
+    invalidateHighlightsOfType(DataDetectorHighlight::Type::TelephoneNumber);
 }
 
-void ServicesOverlayController::invalidateHighlightsOfType(Highlight::Type type)
+void ServicesOverlayController::invalidateHighlightsOfType(DataDetectorHighlight::Type type)
 {
     if (!m_page.settings().serviceControlsEnabled())
         return;
 
-    m_dirtyHighlightTypes |= type;
+    m_dirtyHighlightTypes.add(type);
     m_buildHighlightsTimer.startOneShot(0_s);
 }
 
 void ServicesOverlayController::buildPotentialHighlightsIfNeeded()
 {
-    if (!m_dirtyHighlightTypes)
+    if (m_dirtyHighlightTypes.isEmpty())
         return;
 
-    if (m_dirtyHighlightTypes & Highlight::TelephoneNumberType)
+    if (m_dirtyHighlightTypes.contains(DataDetectorHighlight::Type::TelephoneNumber))
         buildPhoneNumberHighlights();
 
-    if (m_dirtyHighlightTypes & Highlight::SelectionType)
+    if (m_dirtyHighlightTypes.contains(DataDetectorHighlight::Type::Selection))
         buildSelectionHighlight();
 
-    m_dirtyHighlightTypes = 0;
+    m_dirtyHighlightTypes = { };
 
     if (m_potentialHighlights.isEmpty()) {
         if (m_servicesOverlay)
@@ -404,18 +266,18 @@
     determineActiveHighlight(mouseIsOverButton);
 }
 
-bool ServicesOverlayController::mouseIsOverHighlight(Highlight& highlight, bool& mouseIsOverButton) const
+bool ServicesOverlayController::mouseIsOverHighlight(DataDetectorHighlight& highlight, bool& mouseIsOverButton) const
 {
     if (!DataDetectorsLibrary())
         return false;
 
     Boolean onButton;
-    bool hovered = DDHighlightPointIsOnHighlight(highlight.ddHighlight(), (CGPoint)m_mousePosition, &onButton);
+    bool hovered = DDHighlightPointIsOnHighlight(highlight.highlight(), (CGPoint)m_mousePosition, &onButton);
     mouseIsOverButton = onButton;
     return hovered;
 }
 
-Seconds ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown(Highlight* highlight) const
+Seconds ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown(DataDetectorHighlight* highlight) const
 {
     if (!highlight)
         return 0_s;
@@ -429,7 +291,7 @@
     // Highlight hysteresis is only for selection services, because telephone number highlights are already much more stable
     // by virtue of being expanded to include the entire telephone number. However, we will still avoid highlighting
     // telephone numbers while the mouse is down.
-    if (highlight->type() == Highlight::TelephoneNumberType)
+    if (highlight->type() == DataDetectorHighlight::Type::TelephoneNumber)
         return mousePressed ? minimumTimeUntilHighlightShouldBeShown : 0_s;
 
     MonotonicTime now = MonotonicTime::now();
@@ -460,9 +322,9 @@
     m_activeHighlight = nullptr;
 }
 
-void ServicesOverlayController::removeAllPotentialHighlightsOfType(Highlight::Type type)
+void ServicesOverlayController::removeAllPotentialHighlightsOfType(DataDetectorHighlight::Type type)
 {
-    Vector<RefPtr<Highlight>> highlightsToRemove;
+    Vector<RefPtr<DataDetectorHighlight>> highlightsToRemove;
     for (auto& highlight : m_potentialHighlights) {
         if (highlight->type() == type)
             highlightsToRemove.append(highlight);
@@ -479,7 +341,7 @@
         phoneNumberRanges.appendVector(frame->editor().detectedTelephoneNumberRanges());
 
     if (phoneNumberRanges.isEmpty()) {
-        removeAllPotentialHighlightsOfType(Highlight::TelephoneNumberType);
+        removeAllPotentialHighlightsOfType(DataDetectorHighlight::Type::TelephoneNumber);
         return;
     }
 
@@ -486,7 +348,7 @@
     if (!DataDetectorsLibrary())
         return;
 
-    HashSet<RefPtr<Highlight>> newPotentialHighlights;
+    HashSet<RefPtr<DataDetectorHighlight>> newPotentialHighlights;
 
     FrameView& mainFrameView = *mainFrame().view();
 
@@ -510,16 +372,18 @@
 #else
         auto ddHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, &cgRect, 1, mainFrameView.visibleContentRect(), DDHighlightStyleBubbleStandard | DDHighlightStyleStandardIconArrow, YES, NSWritingDirectionNatural, NO, YES));
 #endif
-        newPotentialHighlights.add(Highlight::createForTelephoneNumber(*this, WTFMove(ddHighlight), WTFMove(range)));
+        auto highlight = DataDetectorHighlight::createForTelephoneNumber(m_page, *this, WTFMove(ddHighlight), WTFMove(range));
+        m_highlights.add(highlight.get());
+        newPotentialHighlights.add(WTFMove(highlight));
     }
 
-    replaceHighlightsOfTypePreservingEquivalentHighlights(newPotentialHighlights, Highlight::TelephoneNumberType);
+    replaceHighlightsOfTypePreservingEquivalentHighlights(newPotentialHighlights, DataDetectorHighlight::Type::TelephoneNumber);
 }
 
 void ServicesOverlayController::buildSelectionHighlight()
 {
     if (m_currentSelectionRects.isEmpty()) {
-        removeAllPotentialHighlightsOfType(Highlight::SelectionType);
+        removeAllPotentialHighlightsOfType(DataDetectorHighlight::Type::Selection);
         return;
     }
 
@@ -526,7 +390,7 @@
     if (!DataDetectorsLibrary())
         return;
 
-    HashSet<RefPtr<Highlight>> newPotentialHighlights;
+    HashSet<RefPtr<DataDetectorHighlight>> newPotentialHighlights;
 
     Vector<CGRect> cgRects;
     cgRects.reserveCapacity(m_currentSelectionRects.size());
@@ -553,18 +417,20 @@
 #else
             auto ddHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, cgRects.begin(), cgRects.size(), visibleRect, DDHighlightStyleBubbleNone | DDHighlightStyleStandardIconArrow | DDHighlightStyleButtonShowAlways, YES, NSWritingDirectionNatural, NO, YES));
 #endif
-            newPotentialHighlights.add(Highlight::createForSelection(*this, WTFMove(ddHighlight), WTFMove(*selectionRange)));
+            auto highlight = DataDetectorHighlight::createForSelection(m_page, *this, WTFMove(ddHighlight), WTFMove(*selectionRange));
+            m_highlights.add(highlight.get());
+            newPotentialHighlights.add(WTFMove(highlight));
         }
     }
 
-    replaceHighlightsOfTypePreservingEquivalentHighlights(newPotentialHighlights, Highlight::SelectionType);
+    replaceHighlightsOfTypePreservingEquivalentHighlights(newPotentialHighlights, DataDetectorHighlight::Type::Selection);
 }
 
-void ServicesOverlayController::replaceHighlightsOfTypePreservingEquivalentHighlights(HashSet<RefPtr<Highlight>>& newPotentialHighlights, Highlight::Type type)
+void ServicesOverlayController::replaceHighlightsOfTypePreservingEquivalentHighlights(HashSet<RefPtr<DataDetectorHighlight>>& newPotentialHighlights, DataDetectorHighlight::Type type)
 {
     // If any old Highlights are equivalent (by Range) to a new Highlight, reuse the old
     // one so that any metadata is retained.
-    HashSet<RefPtr<Highlight>> reusedPotentialHighlights;
+    HashSet<RefPtr<DataDetectorHighlight>> reusedPotentialHighlights;
 
     for (auto& oldHighlight : m_potentialHighlights) {
         if (oldHighlight->type() != type)
@@ -571,8 +437,8 @@
             continue;
 
         for (auto& newHighlight : newPotentialHighlights) {
-            if (highlightsAreEquivalent(oldHighlight.get(), newHighlight.get())) {
-                oldHighlight->setDDHighlight(newHighlight->ddHighlight());
+            if (areEquivalent(oldHighlight.get(), newHighlight.get())) {
+                oldHighlight->setHighlight(newHighlight->highlight());
 
                 reusedPotentialHighlights.add(oldHighlight);
                 newPotentialHighlights.remove(newHighlight);
@@ -611,18 +477,9 @@
     return m_page.focusController().focusedOrMainFrame().editor().detectedTelephoneNumberRanges();
 }
 
-bool ServicesOverlayController::highlightsAreEquivalent(const Highlight* a, const Highlight* b)
+DataDetectorHighlight* ServicesOverlayController::findTelephoneNumberHighlightContainingSelectionHighlight(DataDetectorHighlight& selectionHighlight)
 {
-    if (a == b)
-        return true;
-    if (!a || !b)
-        return false;
-    return a->type() == b->type() && a->range() == b->range();
-}
-
-ServicesOverlayController::Highlight* ServicesOverlayController::findTelephoneNumberHighlightContainingSelectionHighlight(Highlight& selectionHighlight)
-{
-    if (selectionHighlight.type() != Highlight::SelectionType)
+    if (selectionHighlight.type() != DataDetectorHighlight::Type::Selection)
         return nullptr;
 
     auto selectionRange = m_page.selection().toNormalizedRange();
@@ -630,7 +487,7 @@
         return nullptr;
 
     for (auto& highlight : m_potentialHighlights) {
-        if (highlight->type() == Highlight::TelephoneNumberType && contains<ComposedTree>(highlight->range(), *selectionRange))
+        if (highlight->type() == DataDetectorHighlight::Type::TelephoneNumber && contains<ComposedTree>(highlight->range(), *selectionRange))
             return highlight.get();
     }
 
@@ -643,13 +500,13 @@
 
     mouseIsOverActiveHighlightButton = false;
 
-    RefPtr<Highlight> newActiveHighlight;
+    RefPtr<DataDetectorHighlight> newActiveHighlight;
 
     for (auto& highlight : m_potentialHighlights) {
-        if (highlight->type() == Highlight::SelectionType) {
+        if (highlight->type() == DataDetectorHighlight::Type::Selection) {
             // If we've already found a new active highlight, and it's
             // a telephone number highlight, prefer that over this selection highlight.
-            if (newActiveHighlight && newActiveHighlight->type() == Highlight::TelephoneNumberType)
+            if (newActiveHighlight && newActiveHighlight->type() == DataDetectorHighlight::Type::TelephoneNumber)
                 continue;
 
             // If this highlight has no compatible services, it can't be active.
@@ -668,8 +525,8 @@
 
     // If our new active highlight is a selection highlight that is completely contained
     // by one of the phone number highlights, we'll make the phone number highlight active even if it's not hovered.
-    if (newActiveHighlight && newActiveHighlight->type() == Highlight::SelectionType) {
-        if (Highlight* containedTelephoneNumberHighlight = findTelephoneNumberHighlightContainingSelectionHighlight(*newActiveHighlight)) {
+    if (newActiveHighlight && newActiveHighlight->type() == DataDetectorHighlight::Type::Selection) {
+        if (DataDetectorHighlight* containedTelephoneNumberHighlight = findTelephoneNumberHighlightContainingSelectionHighlight(*newActiveHighlight)) {
             newActiveHighlight = containedTelephoneNumberHighlight;
 
             // We will always initially choose the telephone number highlight over the selection highlight if the
@@ -678,7 +535,7 @@
         }
     }
 
-    if (!this->highlightsAreEquivalent(m_activeHighlight.get(), newActiveHighlight.get())) {
+    if (!areEquivalent(m_activeHighlight.get(), newActiveHighlight.get())) {
         // When transitioning to a new highlight, we might end up in determineActiveHighlight multiple times
         // before the new highlight actually becomes active. Keep track of the last next-but-not-yet-active
         // highlight, and only reset the active highlight hysteresis when that changes.
@@ -725,7 +582,7 @@
 
     // If the mouse lifted while still over the highlight button that it went down on, then that is a click.
     if (event.type() == PlatformEvent::MouseReleased) {
-        RefPtr<Highlight> mouseDownHighlight = m_currentMouseDownOnButtonHighlight;
+        auto mouseDownHighlight = m_currentMouseDownOnButtonHighlight.copyRef();
         m_currentMouseDownOnButtonHighlight = nullptr;
 
         m_lastMouseUpTime = MonotonicTime::now();
@@ -765,8 +622,8 @@
     if (frame.isMainFrame())
         return;
 
-    invalidateHighlightsOfType(Highlight::TelephoneNumberType);
-    invalidateHighlightsOfType(Highlight::SelectionType);
+    invalidateHighlightsOfType(DataDetectorHighlight::Type::TelephoneNumber);
+    invalidateHighlightsOfType(DataDetectorHighlight::Type::Selection);
     buildPotentialHighlightsIfNeeded();
 
     bool mouseIsOverActiveHighlightButton;
@@ -773,7 +630,7 @@
     determineActiveHighlight(mouseIsOverActiveHighlightButton);
 }
 
-void ServicesOverlayController::handleClick(const IntPoint& clickPoint, Highlight& highlight)
+void ServicesOverlayController::handleClick(const IntPoint& clickPoint, DataDetectorHighlight& highlight)
 {
     FrameView* frameView = mainFrame().view();
     if (!frameView)
@@ -781,7 +638,7 @@
 
     IntPoint windowPoint = frameView->contentsToWindow(clickPoint);
 
-    if (highlight.type() == Highlight::SelectionType) {
+    if (highlight.type() == DataDetectorHighlight::Type::Selection) {
         auto telephoneNumberRanges = telephoneNumberRangesForFocusedFrame();
         Vector<String> selectedTelephoneNumbers;
         selectedTelephoneNumbers.reserveCapacity(telephoneNumberRanges.size());
@@ -789,7 +646,7 @@
             selectedTelephoneNumbers.append(plainText(range));
 
         m_page.chrome().client().handleSelectionServiceClick(m_page.focusController().focusedOrMainFrame().selection(), selectedTelephoneNumbers, windowPoint);
-    } else if (highlight.type() == Highlight::TelephoneNumberType)
+    } else if (highlight.type() == DataDetectorHighlight::Type::TelephoneNumber)
         m_page.chrome().client().handleTelephoneNumberClick(plainText(highlight.range()), windowPoint);
 }
 
@@ -798,18 +655,6 @@
     return m_page.mainFrame();
 }
 
-void ServicesOverlayController::didCreateHighlight(Highlight* highlight)
-{
-    ASSERT(!m_highlights.contains(highlight));
-    m_highlights.add(highlight);
-}
-
-void ServicesOverlayController::willDestroyHighlight(Highlight* highlight)
-{
-    ASSERT(m_highlights.contains(highlight));
-    m_highlights.remove(highlight);
-}
-
 } // namespace WebKit
 
 #endif // (ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION)) && PLATFORM(MAC)

Added: trunk/Source/WebCore/platform/mac/DataDetectorHighlight.h (0 => 277747)


--- trunk/Source/WebCore/platform/mac/DataDetectorHighlight.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/mac/DataDetectorHighlight.h	2021-05-19 20:30:27 UTC (rev 277747)
@@ -0,0 +1,103 @@
+/*
+ * 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
+
+#if ENABLE(DATA_DETECTION) && PLATFORM(MAC)
+
+#import "GraphicsLayerClient.h"
+#import "SimpleRange.h"
+#import <wtf/RefCounted.h>
+#import <wtf/RefPtr.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/WeakPtr.h>
+
+typedef struct __DDHighlight *DDHighlightRef;
+
+namespace WebCore {
+
+class DataDetectorHighlight;
+class FloatRect;
+class GraphicsContext;
+class GraphicsLayer;
+class Page;
+
+class DataDetectorHighlightClient : public CanMakeWeakPtr<DataDetectorHighlightClient> {
+public:
+    virtual ~DataDetectorHighlightClient() = default;
+
+    virtual DataDetectorHighlight* activeHighlight() const = 0;
+};
+
+class DataDetectorHighlight : public RefCounted<DataDetectorHighlight>, private GraphicsLayerClient, public CanMakeWeakPtr<DataDetectorHighlight> {
+    WTF_MAKE_NONCOPYABLE(DataDetectorHighlight);
+public:
+    static Ref<DataDetectorHighlight> createForSelection(Page&, DataDetectorHighlightClient&, RetainPtr<DDHighlightRef>&&, SimpleRange&&);
+    static Ref<DataDetectorHighlight> createForTelephoneNumber(Page&, DataDetectorHighlightClient&, RetainPtr<DDHighlightRef>&&, SimpleRange&&);
+
+    ~DataDetectorHighlight() = default;
+
+    void invalidate();
+
+    DDHighlightRef highlight() const { return m_highlight.get(); }
+    const SimpleRange& range() const { return m_range; }
+    GraphicsLayer& layer() const { return m_graphicsLayer.get(); }
+
+    enum class Type : uint8_t {
+        None = 0,
+        TelephoneNumber = 1 << 0,
+        Selection = 1 << 1,
+    };
+
+    Type type() const { return m_type; }
+
+    void fadeIn();
+    void fadeOut();
+
+    void setHighlight(DDHighlightRef);
+
+private:
+    DataDetectorHighlight(Page&, DataDetectorHighlightClient&, Type, RetainPtr<DDHighlightRef>&&, SimpleRange&&);
+
+    // GraphicsLayerClient
+    void notifyFlushRequired(const GraphicsLayer*) override;
+    void paintContents(const GraphicsLayer*, GraphicsContext&, const FloatRect& inClip, GraphicsLayerPaintBehavior) override;
+    float deviceScaleFactor() const override;
+
+    void didFinishFadeOutAnimation();
+
+    WeakPtr<DataDetectorHighlightClient> m_client;
+    WeakPtr<Page> m_page;
+    RetainPtr<DDHighlightRef> m_highlight;
+    SimpleRange m_range;
+    Ref<GraphicsLayer> m_graphicsLayer;
+    Type m_type { Type::None };
+};
+
+bool areEquivalent(const DataDetectorHighlight*, const DataDetectorHighlight*);
+
+} // namespace WebCore
+
+#endif // ENABLE(DATA_DETECTION) && PLATFORM(MAC)

Added: trunk/Source/WebCore/platform/mac/DataDetectorHighlight.mm (0 => 277747)


--- trunk/Source/WebCore/platform/mac/DataDetectorHighlight.mm	                        (rev 0)
+++ trunk/Source/WebCore/platform/mac/DataDetectorHighlight.mm	2021-05-19 20:30:27 UTC (rev 277747)
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+#import "config.h"
+#import "DataDetectorHighlight.h"
+
+#if ENABLE(DATA_DETECTION) && PLATFORM(MAC)
+
+#import "Chrome.h"
+#import "ChromeClient.h"
+#import "FloatRect.h"
+#import "GraphicsContext.h"
+#import "GraphicsLayer.h"
+#import "GraphicsLayerCA.h"
+#import "GraphicsLayerFactory.h"
+#import "Page.h"
+#import "PlatformCAAnimationCocoa.h"
+#import "PlatformCALayer.h"
+#import <QuartzCore/QuartzCore.h>
+#import <pal/spi/mac/DataDetectorsSPI.h>
+#import <wtf/Seconds.h>
+#import <wtf/SoftLinking.h>
+
+namespace WebCore {
+
+constexpr Seconds highlightFadeAnimationDuration = 300_ms;
+
+Ref<DataDetectorHighlight> DataDetectorHighlight::createForSelection(Page& page, DataDetectorHighlightClient& client, RetainPtr<DDHighlightRef>&& ddHighlight, SimpleRange&& range)
+{
+    return adoptRef(*new DataDetectorHighlight(page, client, DataDetectorHighlight::Type::Selection, WTFMove(ddHighlight), WTFMove(range)));
+}
+
+Ref<DataDetectorHighlight> DataDetectorHighlight::createForTelephoneNumber(Page& page, DataDetectorHighlightClient& client, RetainPtr<DDHighlightRef>&& ddHighlight, SimpleRange&& range)
+{
+    return adoptRef(*new DataDetectorHighlight(page, client, DataDetectorHighlight::Type::TelephoneNumber, WTFMove(ddHighlight), WTFMove(range)));
+}
+
+DataDetectorHighlight::DataDetectorHighlight(Page& page, DataDetectorHighlightClient& client, Type type, RetainPtr<DDHighlightRef>&& ddHighlight, SimpleRange&& range)
+    : m_client(makeWeakPtr(client))
+    , m_page(makeWeakPtr(page))
+    , m_range(WTFMove(range))
+    , m_graphicsLayer(GraphicsLayer::create(page.chrome().client().graphicsLayerFactory(), *this))
+    , m_type(type)
+{
+    ASSERT(ddHighlight);
+
+    m_graphicsLayer->setDrawsContent(true);
+
+    setHighlight(ddHighlight.get());
+
+    // Set directly on the PlatformCALayer so that when we leave the 'from' value implicit
+    // in our animations, we get the right initial value regardless of flush timing.
+    downcast<GraphicsLayerCA>(layer()).platformCALayer()->setOpacity(0);
+}
+
+void DataDetectorHighlight::setHighlight(DDHighlightRef highlight)
+{
+    if (!DataDetectorsLibrary())
+        return;
+
+    if (!m_client)
+        return;
+
+    m_highlight = highlight;
+
+    if (!m_highlight)
+        return;
+
+    CGRect highlightBoundingRect = DDHighlightGetBoundingRect(m_highlight.get());
+    m_graphicsLayer->setPosition(FloatPoint(highlightBoundingRect.origin));
+    m_graphicsLayer->setSize(FloatSize(highlightBoundingRect.size));
+
+    m_graphicsLayer->setNeedsDisplay();
+}
+
+void DataDetectorHighlight::invalidate()
+{
+    layer().removeFromParent();
+    m_client = nullptr;
+    m_page = nullptr;
+}
+
+void DataDetectorHighlight::notifyFlushRequired(const GraphicsLayer*)
+{
+    if (!m_page)
+        return;
+
+    m_page->scheduleRenderingUpdate(RenderingUpdateStep::LayerFlush);
+}
+
+void DataDetectorHighlight::paintContents(const GraphicsLayer*, GraphicsContext& graphicsContext, const FloatRect&, GraphicsLayerPaintBehavior)
+{
+    if (!DataDetectorsLibrary())
+        return;
+
+    // FIXME: This needs to be moved into GraphicsContext as a DisplayList-compatible drawing command.
+    if (!graphicsContext.hasPlatformContext()) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+
+    CGContextRef cgContext = graphicsContext.platformContext();
+
+    ALLOW_DEPRECATED_DECLARATIONS_BEGIN
+    CGLayerRef highlightLayer = DDHighlightGetLayerWithContext(highlight(), cgContext);
+    ALLOW_DEPRECATED_DECLARATIONS_END
+    CGRect highlightBoundingRect = DDHighlightGetBoundingRect(highlight());
+    highlightBoundingRect.origin = CGPointZero;
+
+    CGContextDrawLayerInRect(cgContext, highlightBoundingRect, highlightLayer);
+}
+
+float DataDetectorHighlight::deviceScaleFactor() const
+{
+    if (!m_page)
+        return 1;
+
+    return m_page->deviceScaleFactor();
+}
+
+void DataDetectorHighlight::fadeIn()
+{
+    RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
+    [animation setDuration:highlightFadeAnimationDuration.seconds()];
+    [animation setFillMode:kCAFillModeForwards];
+    [animation setRemovedOnCompletion:false];
+    [animation setToValue:@1];
+
+    auto platformAnimation = PlatformCAAnimationCocoa::create(animation.get());
+    downcast<GraphicsLayerCA>(layer()).platformCALayer()->addAnimationForKey("FadeHighlightIn", platformAnimation.get());
+}
+
+void DataDetectorHighlight::fadeOut()
+{
+    RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
+    [animation setDuration:highlightFadeAnimationDuration.seconds()];
+    [animation setFillMode:kCAFillModeForwards];
+    [animation setRemovedOnCompletion:false];
+    [animation setToValue:@0];
+
+    [CATransaction begin];
+    [CATransaction setCompletionBlock:[protectedSelf = makeRef(*this)]() mutable {
+        protectedSelf->didFinishFadeOutAnimation();
+    }];
+
+    auto platformAnimation = PlatformCAAnimationCocoa::create(animation.get());
+    downcast<GraphicsLayerCA>(layer()).platformCALayer()->addAnimationForKey("FadeHighlightOut", platformAnimation.get());
+    [CATransaction commit];
+}
+
+void DataDetectorHighlight::didFinishFadeOutAnimation()
+{
+    if (!m_client)
+        return;
+
+    if (m_client->activeHighlight() == this)
+        return;
+
+    layer().removeFromParent();
+}
+
+bool areEquivalent(const DataDetectorHighlight* a, const DataDetectorHighlight* b)
+{
+    if (a == b)
+        return true;
+
+    if (!a || !b)
+        return false;
+
+    return a->type() == b->type() && a->range() == b->range();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(DATA_DETECTION) && PLATFORM(MAC)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to