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)