+2014-08-12 Tim Horton <timothy_horton@apple.com>
+
+ Add a fade transition to services highlights
+ https://bugs.webkit.org/show_bug.cgi?id=135829
+ <rdar://problem/17935736>
+
+ Reviewed by Enrica Casucci.
+
+ Add a smooth fade to highlight installation and uninstallation.
+ To do so, we make each highlight paint into its own small layer.
+
+ * WebProcess/WebPage/PageOverlay.cpp:
+ (WebKit::PageOverlay::layer):
+ * WebProcess/WebPage/PageOverlay.h:
+ * WebProcess/WebPage/PageOverlayController.cpp:
+ (WebKit::PageOverlayController::layerForOverlay):
+ * WebProcess/WebPage/PageOverlayController.h:
+ Expose the GraphicsLayer on PageOverlay.
+
+ * WebProcess/WebPage/ServicesOverlayController.h:
+ (WebKit::ServicesOverlayController::Highlight::layer):
+ (WebKit::ServicesOverlayController::activeHighlight):
+ (WebKit::ServicesOverlayController::webPage):
+ (WebKit::ServicesOverlayController::Highlight::Highlight): Deleted.
+
+ * WebProcess/WebPage/mac/ServicesOverlayController.mm:
+ (WebKit::ServicesOverlayController::Highlight::createForSelection):
+ (WebKit::ServicesOverlayController::Highlight::createForTelephoneNumber):
+ (WebKit::ServicesOverlayController::Highlight::Highlight):
+ Highlights now own a GraphicsLayer, which are later installed
+ as sublayers of the ServicesOverlayController's PageOverlay layer.
+ These layers are sized and positioned according to the DDHighlight's bounds.
+
+ (WebKit::ServicesOverlayController::Highlight::~Highlight):
+ (WebKit::ServicesOverlayController::Highlight::invalidate):
+ ServicesOverlayController will invalidate any remaining highlights
+ when it is torn down, so they can clear their backpointers.
+
+ (WebKit::ServicesOverlayController::Highlight::notifyFlushRequired):
+ Forward flush notifications to the DrawingArea.
+
+ (WebKit::ServicesOverlayController::Highlight::paintContents):
+ Paint the DDHighlight into the layer. Translation is done by the layer position,
+ so we zero the bounds origin when painting.
+
+ (WebKit::ServicesOverlayController::Highlight::deviceScaleFactor):
+ Forward the deviceScaleFactor so that things are painted at the right scale.
+
+ (WebKit::ServicesOverlayController::Highlight::fadeIn):
+ (WebKit::ServicesOverlayController::Highlight::fadeOut):
+ Apply a fade animation to the layer.
+
+ (WebKit::ServicesOverlayController::Highlight::didFinishFadeOutAnimation):
+ When the fade completes, unparent the layer, unless it has become active again.
+
+ (WebKit::ServicesOverlayController::ServicesOverlayController):
+ (WebKit::ServicesOverlayController::~ServicesOverlayController):
+ Invalidate all highlights, so they can clear their backpointers.
+
+ (WebKit::ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown):
+ Make remainingTimeUntilHighlightShouldBeShown act upon a particular highlight
+ instead of always the active highlight.
+
+ (WebKit::ServicesOverlayController::determineActiveHighlightTimerFired): Rename.
+
+ (WebKit::ServicesOverlayController::drawRect):
+ drawRect is no longer called and will no longer do anything; all of the
+ painting is done in sublayers.
+
+ (WebKit::ServicesOverlayController::buildPhoneNumberHighlights):
+ Ensure that phone number Highlights stay stable even while the selection
+ changes, by comparing the underlying Ranges and keeping around old Highlights
+ that match the new ones. This enables us to e.g. fade in while changing
+ the selection within a phone number.
+
+ (WebKit::ServicesOverlayController::buildSelectionHighlight):
+ (WebKit::ServicesOverlayController::didRebuildPotentialHighlights):
+ (WebKit::ServicesOverlayController::createOverlayIfNeeded):
+ Don't call setNeedsDisplay; the overlay doesn't have backing store.
+ Instead, call determineActiveHighlight, which will install/uninstall
+ highlights as necessary.
+
+ (WebKit::ServicesOverlayController::determineActiveHighlight):
+ Apply fade in/fade out to the overlays.
+ Keep track of which highlight we're going to activate, until the hysteresis
+ delay is up, then actually make it active/parent it/fade it in.
+ We now will have no active highlight between the fade out of the previous one
+ and the fade in of the new one (during the hysteresis delay).
+
+ (WebKit::ServicesOverlayController::mouseEvent):
+ The overlay now will not become active until the delay is up, so we don't
+ need to check it again here.
+
+ (WebKit::ServicesOverlayController::handleClick):
+ (WebKit::ServicesOverlayController::didCreateHighlight):
+ (WebKit::ServicesOverlayController::willDestroyHighlight):
+ (WebKit::ServicesOverlayController::repaintHighlightTimerFired): Deleted.
+ (WebKit::ServicesOverlayController::drawHighlight): Deleted.
+
2014-08-11 Andy Estes <aestes@apple.com>
[iOS] Get rid of iOS.xcconfig
#include "PageOverlay.h"
#include "WebFrame.h"
+#include <WebCore/GraphicsLayerClient.h>
#include <WebCore/Range.h>
#include <WebCore/Timer.h>
#include <wtf/RefCounted.h>
void selectionRectsDidChange(const Vector<WebCore::LayoutRect>&, const Vector<WebCore::GapRects>&, bool isTextOnly);
private:
- class Highlight : public RefCounted<Highlight> {
+ class Highlight : public RefCounted<Highlight>, private WebCore::GraphicsLayerClient {
WTF_MAKE_NONCOPYABLE(Highlight);
public:
- static PassRefPtr<Highlight> createForSelection(RetainPtr<DDHighlightRef>);
- static PassRefPtr<Highlight> createForTelephoneNumber(RetainPtr<DDHighlightRef>, PassRefPtr<WebCore::Range>);
+ static PassRefPtr<Highlight> createForSelection(ServicesOverlayController&, RetainPtr<DDHighlightRef>);
+ static PassRefPtr<Highlight> createForTelephoneNumber(ServicesOverlayController&, RetainPtr<DDHighlightRef>, PassRefPtr<WebCore::Range>);
+ ~Highlight();
+
+ void invalidate();
DDHighlightRef ddHighlight() const { return m_ddHighlight.get(); }
WebCore::Range* range() const { return m_range.get(); }
+ WebCore::GraphicsLayer* layer() const { return m_graphicsLayer.get(); }
enum class Type {
TelephoneNumber,
};
Type type() const { return m_type; }
+ void fadeIn();
+ void fadeOut();
+
private:
- explicit Highlight(Type type, RetainPtr<DDHighlightRef> ddHighlight, PassRefPtr<WebCore::Range> range)
- : m_ddHighlight(ddHighlight)
- , m_range(range)
- , m_type(type)
- {
- ASSERT(m_ddHighlight);
- ASSERT(type != Type::TelephoneNumber || m_range);
- }
+ explicit Highlight(ServicesOverlayController&, Type, RetainPtr<DDHighlightRef>, PassRefPtr<WebCore::Range>);
+
+ // GraphicsLayerClient
+ virtual void notifyAnimationStarted(const WebCore::GraphicsLayer*, double time) override { }
+ virtual void notifyFlushRequired(const WebCore::GraphicsLayer*) override;
+ virtual void paintContents(const WebCore::GraphicsLayer*, WebCore::GraphicsContext&, WebCore::GraphicsLayerPaintingPhase, const WebCore::FloatRect& inClip) override;
+ virtual float deviceScaleFactor() const override;
+
+ void didFinishFadeOutAnimation();
RetainPtr<DDHighlightRef> m_ddHighlight;
RefPtr<WebCore::Range> m_range;
+ std::unique_ptr<WebCore::GraphicsLayer> m_graphicsLayer;
Type m_type;
+ ServicesOverlayController* m_controller;
};
// PageOverlay::Client
void determineActiveHighlight(bool& mouseIsOverButton);
void clearActiveHighlight();
+ Highlight* activeHighlight() const { return m_activeHighlight.get(); }
bool hasRelevantSelectionServices();
bool mouseIsOverHighlight(Highlight&, bool& mouseIsOverButton) const;
- std::chrono::milliseconds remainingTimeUntilHighlightShouldBeShown() const;
- void repaintHighlightTimerFired(WebCore::Timer<ServicesOverlayController>&);
+ std::chrono::milliseconds remainingTimeUntilHighlightShouldBeShown(Highlight*) const;
+ void determineActiveHighlightTimerFired(WebCore::Timer<ServicesOverlayController>&);
static bool highlightsAreEquivalent(const Highlight* a, const Highlight* b);
Vector<RefPtr<WebCore::Range>> telephoneNumberRangesForFocusedFrame();
+ void didCreateHighlight(Highlight*);
+ void willDestroyHighlight(Highlight*);
+ void didFinishFadingOutHighlight(Highlight*);
+
+ WebPage& webPage() const { return m_webPage; }
+
WebPage& m_webPage;
PageOverlay* m_servicesOverlay;
RefPtr<Highlight> m_activeHighlight;
+ RefPtr<Highlight> m_nextActiveHighlight;
HashSet<RefPtr<Highlight>> m_potentialHighlights;
+ HashSet<RefPtr<Highlight>> m_animatingHighlights;
+
+ HashSet<Highlight*> m_highlights;
Vector<WebCore::LayoutRect> m_currentSelectionRects;
bool m_isTextOnly;
std::chrono::steady_clock::time_point m_lastSelectionChangeTime;
- std::chrono::steady_clock::time_point m_lastActiveHighlightChangeTime;
+ std::chrono::steady_clock::time_point m_nextActiveHighlightChangeTime;
RefPtr<Highlight> m_currentMouseDownOnButtonHighlight;
WebCore::IntPoint m_mousePosition;
- WebCore::Timer<ServicesOverlayController> m_repaintHighlightTimer;
+ WebCore::Timer<ServicesOverlayController> m_determineActiveHighlightTimer;
};
} // namespace WebKit
#import "Logging.h"
#import "WebPage.h"
#import "WebProcess.h"
+#import <QuartzCore/QuartzCore.h>
#import <WebCore/Document.h>
#import <WebCore/FloatQuad.h>
#import <WebCore/FocusController.h>
#import <WebCore/FrameView.h>
#import <WebCore/GapRects.h>
#import <WebCore/GraphicsContext.h>
+#import <WebCore/GraphicsLayer.h>
+#import <WebCore/GraphicsLayerCA.h>
#import <WebCore/MainFrame.h>
+#import <WebCore/PlatformCAAnimationMac.h>
#import <WebCore/SoftLinking.h>
#if __has_include(<DataDetectors/DDHighlightDrawing.h>)
#import <DataDetectors/DDHighlightDrawing_Private.h>
#endif
+const float highlightFadeAnimationDuration = 0.3;
+
typedef NSUInteger DDHighlightStyle;
static const DDHighlightStyle DDHighlightNoOutlineWithArrow = (1 << 16);
static const DDHighlightStyle DDHighlightOutlineWithArrow = (1 << 16) | 1;
namespace WebKit {
-PassRefPtr<ServicesOverlayController::Highlight> ServicesOverlayController::Highlight::createForSelection(RetainPtr<DDHighlightRef> ddHighlight)
+PassRefPtr<ServicesOverlayController::Highlight> ServicesOverlayController::Highlight::createForSelection(ServicesOverlayController& controller, RetainPtr<DDHighlightRef> ddHighlight)
+{
+ return adoptRef(new Highlight(controller, Type::Selection, ddHighlight, nullptr));
+}
+
+PassRefPtr<ServicesOverlayController::Highlight> ServicesOverlayController::Highlight::createForTelephoneNumber(ServicesOverlayController& controller, RetainPtr<DDHighlightRef> ddHighlight, PassRefPtr<Range> range)
+{
+ return adoptRef(new Highlight(controller, Type::TelephoneNumber, ddHighlight, range));
+}
+
+ServicesOverlayController::Highlight::Highlight(ServicesOverlayController& controller, Type type, RetainPtr<DDHighlightRef> ddHighlight, PassRefPtr<WebCore::Range> range)
+ : m_ddHighlight(ddHighlight)
+ , m_range(range)
+ , m_type(type)
+ , m_controller(&controller)
+{
+ ASSERT(m_ddHighlight);
+ ASSERT(type != Type::TelephoneNumber || m_range);
+
+ DrawingArea* drawingArea = controller.webPage().drawingArea();
+ m_graphicsLayer = GraphicsLayer::create(drawingArea ? drawingArea->graphicsLayerFactory() : nullptr, *this);
+ m_graphicsLayer->setDrawsContent(true);
+ m_graphicsLayer->setNeedsDisplay();
+
+ CGRect highlightBoundingRect = DDHighlightGetBoundingRect(ddHighlight.get());
+ m_graphicsLayer->setPosition(FloatPoint(highlightBoundingRect.origin));
+ m_graphicsLayer->setSize(FloatSize(highlightBoundingRect.size));
+
+ // 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.
+ toGraphicsLayerCA(layer())->platformCALayer()->setOpacity(0);
+
+ controller.didCreateHighlight(this);
+}
+
+ServicesOverlayController::Highlight::~Highlight()
+{
+ if (m_controller)
+ m_controller->willDestroyHighlight(this);
+}
+
+void ServicesOverlayController::Highlight::invalidate()
{
- return adoptRef(new Highlight(Type::Selection, ddHighlight, nullptr));
+ layer()->removeFromParent();
+ m_controller = nullptr;
}
-PassRefPtr<ServicesOverlayController::Highlight> ServicesOverlayController::Highlight::createForTelephoneNumber(RetainPtr<DDHighlightRef> ddHighlight, PassRefPtr<Range> range)
+void ServicesOverlayController::Highlight::notifyFlushRequired(const GraphicsLayer*)
{
- return adoptRef(new Highlight(Type::TelephoneNumber, ddHighlight, range));
+ if (!m_controller)
+ return;
+
+ if (DrawingArea* drawingArea = m_controller->webPage().drawingArea())
+ drawingArea->scheduleCompositingLayerFlush();
+}
+
+void ServicesOverlayController::Highlight::paintContents(const GraphicsLayer*, GraphicsContext& graphicsContext, GraphicsLayerPaintingPhase, const FloatRect& inClip)
+{
+ CGContextRef cgContext = graphicsContext.platformContext();
+
+ CGLayerRef highlightLayer = DDHighlightGetLayerWithContext(ddHighlight(), cgContext);
+ CGRect highlightBoundingRect = DDHighlightGetBoundingRect(ddHighlight());
+ highlightBoundingRect.origin = CGPointZero;
+
+ CGContextDrawLayerInRect(cgContext, highlightBoundingRect, highlightLayer);
+}
+
+float ServicesOverlayController::Highlight::deviceScaleFactor() const
+{
+ if (!m_controller)
+ return 1;
+
+ return m_controller->webPage().deviceScaleFactor();
+}
+
+void ServicesOverlayController::Highlight::fadeIn()
+{
+ RetainPtr<CABasicAnimation> animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
+ [animation setDuration:highlightFadeAnimationDuration];
+ [animation setFillMode:kCAFillModeForwards];
+ [animation setRemovedOnCompletion:false];
+ [animation setToValue:@1];
+
+ RefPtr<PlatformCAAnimation> platformAnimation = PlatformCAAnimationMac::create(animation.get());
+ toGraphicsLayerCA(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();
+ }];
+
+ RefPtr<PlatformCAAnimation> platformAnimation = PlatformCAAnimationMac::create(animation.get());
+ toGraphicsLayerCA(layer())->platformCALayer()->addAnimationForKey("FadeHighlightOut", platformAnimation.get());
+ [CATransaction commit];
+}
+
+void ServicesOverlayController::Highlight::didFinishFadeOutAnimation()
+{
+ if (!m_controller)
+ return;
+
+ if (m_controller->activeHighlight() == this)
+ return;
+
+ layer()->removeFromParent();
}
static IntRect textQuadsToBoundingRectForRange(Range& range)
: m_webPage(webPage)
, m_servicesOverlay(nullptr)
, m_isTextOnly(false)
- , m_repaintHighlightTimer(this, &ServicesOverlayController::repaintHighlightTimerFired)
+ , m_determineActiveHighlightTimer(this, &ServicesOverlayController::determineActiveHighlightTimerFired)
{
}
ServicesOverlayController::~ServicesOverlayController()
{
+ for (auto& highlight : m_highlights)
+ highlight->invalidate();
}
void ServicesOverlayController::pageOverlayDestroyed(PageOverlay*)
return hovered;
}
-std::chrono::milliseconds ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown() const
+std::chrono::milliseconds ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown(Highlight* highlight) const
{
+ if (!highlight)
+ return std::chrono::milliseconds::zero();
+
// 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.
- if (m_activeHighlight->type() == Highlight::Type::TelephoneNumber)
+ if (highlight->type() == Highlight::Type::TelephoneNumber)
return std::chrono::milliseconds::zero();
std::chrono::steady_clock::duration minimumTimeUntilHighlightShouldBeShown = 200_ms;
auto now = std::chrono::steady_clock::now();
auto timeSinceLastSelectionChange = now - m_lastSelectionChangeTime;
- auto timeSinceHighlightBecameActive = now - m_lastActiveHighlightChangeTime;
+ auto timeSinceHighlightBecameActive = now - m_nextActiveHighlightChangeTime;
return std::chrono::duration_cast<std::chrono::milliseconds>(std::max(minimumTimeUntilHighlightShouldBeShown - timeSinceLastSelectionChange, minimumTimeUntilHighlightShouldBeShown - timeSinceHighlightBecameActive));
}
-void ServicesOverlayController::repaintHighlightTimerFired(WebCore::Timer<ServicesOverlayController>&)
-{
- if (m_servicesOverlay)
- m_servicesOverlay->setNeedsDisplay();
-}
-
-void ServicesOverlayController::drawHighlight(Highlight& highlight, WebCore::GraphicsContext& graphicsContext)
+void ServicesOverlayController::determineActiveHighlightTimerFired(Timer<ServicesOverlayController>&)
{
bool mouseIsOverButton;
- if (!mouseIsOverHighlight(highlight, mouseIsOverButton)) {
- LOG(Services, "ServicesOverlayController::drawHighlight - Mouse is not over highlight, so drawing nothing");
- return;
- }
-
- auto remainingTimeUntilHighlightShouldBeShown = this->remainingTimeUntilHighlightShouldBeShown();
- if (remainingTimeUntilHighlightShouldBeShown > std::chrono::steady_clock::duration::zero()) {
- m_repaintHighlightTimer.startOneShot(remainingTimeUntilHighlightShouldBeShown);
- return;
- }
-
- CGContextRef cgContext = graphicsContext.platformContext();
-
- CGLayerRef highlightLayer = DDHighlightGetLayerWithContext(highlight.ddHighlight(), cgContext);
- CGRect highlightBoundingRect = DDHighlightGetBoundingRect(highlight.ddHighlight());
-
- CGContextDrawLayerInRect(cgContext, highlightBoundingRect, highlightLayer);
+ determineActiveHighlight(mouseIsOverButton);
}
-void ServicesOverlayController::drawRect(PageOverlay* overlay, WebCore::GraphicsContext& graphicsContext, const WebCore::IntRect& dirtyRect)
+void ServicesOverlayController::drawRect(PageOverlay* overlay, GraphicsContext& graphicsContext, const IntRect& dirtyRect)
{
- bool mouseIsOverButton;
- determineActiveHighlight(mouseIsOverButton);
-
- if (m_activeHighlight)
- drawHighlight(*m_activeHighlight, graphicsContext);
}
void ServicesOverlayController::clearActiveHighlight()
void ServicesOverlayController::buildPhoneNumberHighlights()
{
- removeAllPotentialHighlightsOfType(Highlight::Type::TelephoneNumber);
+ HashSet<RefPtr<Highlight>> newPotentialHighlights;
Frame* mainFrame = m_webPage.mainFrame();
FrameView& mainFrameView = *mainFrame->view();
CGRect cgRect = rect;
RetainPtr<DDHighlightRef> ddHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, &cgRect, 1, mainFrameView.visibleContentRect(), DDHighlightOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
- m_potentialHighlights.add(Highlight::createForTelephoneNumber(ddHighlight, range));
+ newPotentialHighlights.add(Highlight::createForTelephoneNumber(*this, ddHighlight, range));
+ }
+ }
+
+ // 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;
+
+ for (auto& oldHighlight : m_potentialHighlights) {
+ if (oldHighlight->type() != Highlight::Type::TelephoneNumber)
+ continue;
+
+ for (auto& newHighlight : newPotentialHighlights) {
+ if (highlightsAreEquivalent(oldHighlight.get(), newHighlight.get())) {
+ reusedPotentialHighlights.add(oldHighlight);
+ newPotentialHighlights.remove(newHighlight);
+ break;
+ }
}
}
+ removeAllPotentialHighlightsOfType(Highlight::Type::TelephoneNumber);
+
+ m_potentialHighlights.add(newPotentialHighlights.begin(), newPotentialHighlights.end());
+ m_potentialHighlights.add(reusedPotentialHighlights.begin(), reusedPotentialHighlights.end());
+
didRebuildPotentialHighlights();
}
CGRect visibleRect = m_webPage.corePage()->mainFrame().view()->visibleContentRect();
RetainPtr<DDHighlightRef> ddHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, cgRects.begin(), cgRects.size(), visibleRect, DDHighlightNoOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
- m_potentialHighlights.add(Highlight::createForSelection(ddHighlight));
+ m_potentialHighlights.add(Highlight::createForSelection(*this, ddHighlight));
}
didRebuildPotentialHighlights();
return;
createOverlayIfNeeded();
+
+ bool mouseIsOverButton;
+ determineActiveHighlight(mouseIsOverButton);
}
void ServicesOverlayController::createOverlayIfNeeded()
{
- if (m_servicesOverlay) {
- m_servicesOverlay->setNeedsDisplay();
+ if (m_servicesOverlay)
return;
- }
RefPtr<PageOverlay> overlay = PageOverlay::create(this, PageOverlay::OverlayType::Document);
m_servicesOverlay = overlay.get();
m_webPage.installPageOverlay(overlay.release(), PageOverlay::FadeMode::DoNotFade);
- m_servicesOverlay->setNeedsDisplay();
}
Vector<RefPtr<Range>> ServicesOverlayController::telephoneNumberRangesForFocusedFrame()
{
mouseIsOverActiveHighlightButton = false;
- RefPtr<Highlight> oldActiveHighlight = m_activeHighlight.release();
+ RefPtr<Highlight> newActiveHighlight;
for (auto& highlight : m_potentialHighlights) {
if (highlight->type() == Highlight::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 (m_activeHighlight && m_activeHighlight->type() == Highlight::Type::TelephoneNumber)
+ if (newActiveHighlight && newActiveHighlight->type() == Highlight::Type::TelephoneNumber)
continue;
// If this highlight has no compatible services, it can't be active, unless we have telephone number highlights to show in the combined menu.
if (!mouseIsOverHighlight(*highlight, mouseIsOverButton))
continue;
- m_activeHighlight = highlight;
+ newActiveHighlight = highlight;
mouseIsOverActiveHighlightButton = mouseIsOverButton;
}
- if (!highlightsAreEquivalent(oldActiveHighlight.get(), m_activeHighlight.get())) {
- m_lastActiveHighlightChangeTime = std::chrono::steady_clock::now();
- m_servicesOverlay->setNeedsDisplay();
+ if (!this->highlightsAreEquivalent(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.
+ if (m_nextActiveHighlight != newActiveHighlight) {
+ m_nextActiveHighlight = newActiveHighlight;
+ m_nextActiveHighlightChangeTime = std::chrono::steady_clock::now();
+ }
+
m_currentMouseDownOnButtonHighlight = nullptr;
+
+ if (m_activeHighlight) {
+ m_activeHighlight->fadeOut();
+ m_activeHighlight = nullptr;
+ }
+
+ auto remainingTimeUntilHighlightShouldBeShown = this->remainingTimeUntilHighlightShouldBeShown(newActiveHighlight.get());
+ if (remainingTimeUntilHighlightShouldBeShown > std::chrono::steady_clock::duration::zero()) {
+ m_determineActiveHighlightTimer.startOneShot(remainingTimeUntilHighlightShouldBeShown);
+ return;
+ }
+
+ m_activeHighlight = m_nextActiveHighlight.release();
+
+ if (m_activeHighlight) {
+ m_servicesOverlay->layer()->addChild(m_activeHighlight->layer());
+ m_activeHighlight->fadeIn();
+ }
}
}
RefPtr<Highlight> mouseDownHighlight = m_currentMouseDownOnButtonHighlight;
m_currentMouseDownOnButtonHighlight = nullptr;
- if (mouseIsOverActiveHighlightButton && mouseDownHighlight && remainingTimeUntilHighlightShouldBeShown() <= std::chrono::steady_clock::duration::zero()) {
+ if (mouseIsOverActiveHighlightButton && mouseDownHighlight) {
handleClick(m_mousePosition, *mouseDownHighlight);
return true;
}
if (event.type() == WebEvent::MouseDown) {
if (m_activeHighlight && mouseIsOverActiveHighlightButton) {
m_currentMouseDownOnButtonHighlight = m_activeHighlight;
- m_servicesOverlay->setNeedsDisplay();
return true;
}
return false;
}
-void ServicesOverlayController::handleClick(const WebCore::IntPoint& clickPoint, Highlight& highlight)
+void ServicesOverlayController::handleClick(const IntPoint& clickPoint, Highlight& highlight)
{
FrameView* frameView = m_webPage.mainFrameView();
if (!frameView)
m_webPage.handleTelephoneNumberClick(highlight.range()->text(), windowPoint);
}
+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 // #if ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(MAC)