Phone number highlights should always be visible if the mouse hovers over.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Jul 2014 16:19:56 +0000 (16:19 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Jul 2014 16:19:56 +0000 (16:19 +0000)
<rdar://problem/17527476> and https://bugs.webkit.org/show_bug.cgi?id=134784

Reviewed by Tim Horton.

Source/WebCore:
* WebCore.exp.in:

Source/WebKit2:
This is a fairly extensive rewrite of ServicesOverlayController.
It allows one selection highlight for the entire selection, and as many telephone number highlights as there are numbers.
If a telephone number highlight is hovered over, it wins and is painted.
If no telephone number highlight is hovered but the selection highlight is, then it is painted.

The purposes of each method are self evident by their name, and the concepts are mostly the same as they used to be.

The exception is establishHoveredTelephoneHighlight which gets a more detailed explanation below.

* Platform/Logging.h: Add a Services logging channel.

* WebProcess/WebPage/ServicesOverlayController.h:
(WebKit::TelephoneNumberData::TelephoneNumberData):

* WebProcess/WebPage/mac/ServicesOverlayController.mm:
(WebKit::ServicesOverlayController::ServicesOverlayController):
(WebKit::ServicesOverlayController::selectionRectsDidChange):
(WebKit::ServicesOverlayController::selectedTelephoneNumberRangesChanged):
(WebKit::ServicesOverlayController::clearHighlightState):
(WebKit::ServicesOverlayController::drawRect):
(WebKit::ServicesOverlayController::drawSelectionHighlight):
(WebKit::ServicesOverlayController::maybeDrawTelephoneNumberHighlight):
(WebKit::ServicesOverlayController::drawHighlight):
(WebKit::ServicesOverlayController::clearSelectionHighlight):
(WebKit::ServicesOverlayController::clearHoveredTelephoneNumberHighlight):
(WebKit::ServicesOverlayController::establishHoveredTelephoneHighlight): Starts walking the telephone number ranges and
    creating a highlight for each one that doesn’t already have a highlight. If that highlight is also being hovered by
    the mouse, then it is set as the hovered telephone number highlight and the method stops creating new highlights.
(WebKit::ServicesOverlayController::maybeCreateSelectionHighlight):
(WebKit::ServicesOverlayController::mouseEvent):
(WebKit::ServicesOverlayController::handleClick):
(WebKit::ServicesOverlayController::drawTelephoneNumberHighlight): Deleted.
(WebKit::ServicesOverlayController::drawCurrentHighlight): Deleted.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@170966 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebKit2/ChangeLog
Source/WebKit2/Platform/Logging.h
Source/WebKit2/WebProcess/WebPage/ServicesOverlayController.h
Source/WebKit2/WebProcess/WebPage/mac/ServicesOverlayController.mm

index f640b12082006b1bcbe4743882686a9a7e7a4b6c..741f061736358fd0f41c1cfb76edfba7506f6a16 100644 (file)
@@ -1,3 +1,12 @@
+2014-07-10  Brady Eidson  <beidson@apple.com>
+
+        Phone number highlights should always be visible if the mouse hovers over.
+        <rdar://problem/17527476> and https://bugs.webkit.org/show_bug.cgi?id=134784
+
+        Reviewed by Tim Horton.
+
+        * WebCore.exp.in:
+
 2014-07-10  Timothy Horton  <timothy_horton@apple.com>
 
         Assertions or crashes under _takeViewSnapshot when restoring windows
index 0addc015a69fc9585385c613ebd3f2cf87a85953..09cf22339d333b590114dfecee5d747c65fcb354 100644 (file)
@@ -459,6 +459,7 @@ __ZN7WebCore14TileController23setTileDebugBorderColorENS_5ColorE
 __ZN7WebCore14TileController23setTileDebugBorderWidthEf
 __ZN7WebCore14TileController27tileCacheLayerBoundsChangedEv
 __ZN7WebCore14TileController6createEPNS_15PlatformCALayerE
+__ZN7WebCore14areRangesEqualEPKNS_5RangeES2_
 __ZN7WebCore14decodeHostNameEP8NSString
 __ZN7WebCore14encodeHostNameEP8NSString
 __ZN7WebCore14endOfParagraphERKNS_15VisiblePositionENS_27EditingBoundaryCrossingRuleE
index 547d73c8a44df8a24306ca09d5766bf8100cc52d..77ff4f915afeb4e6b8a6d0f8d6eba855ebd76d10 100644 (file)
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
 2014-07-10  Timothy Horton  <timothy_horton@apple.com>
 
         Assertions or crashes under _takeViewSnapshot when restoring windows
         (-[WKPreferences _setTiledScrollingIndicatorVisible:]):
         * UIProcess/API/Cocoa/WKPreferencesPrivate.h:
         Expose layer borders, repaint counters, and the tiled scrolling indicator on WKPreferences, as SPI.
+=======
+2014-07-09  Brady Eidson  <beidson@apple.com>
+
+        Phone number highlights should always be visible if the mouse hovers over.
+        <rdar://problem/17527476> and https://bugs.webkit.org/show_bug.cgi?id=134784
+
+        Reviewed by Tim Horton.
+
+        This is a fairly extensive rewrite of ServicesOverlayController.
+        It allows one selection highlight for the entire selection, and as many telephone number highlights as there are numbers.
+        If a telephone number highlight is hovered over, it wins and is painted.
+        If no telephone number highlight is hovered but the selection highlight is, then it is painted.
+       
+        The purposes of each method are self evident by their name, and the concepts are mostly the same as they used to be.
+
+        The exception is establishHoveredTelephoneHighlight which gets a more detailed explanation below.
+        
+        * Platform/Logging.h: Add a Services logging channel.
+
+        * WebProcess/WebPage/ServicesOverlayController.h:
+        (WebKit::TelephoneNumberData::TelephoneNumberData):
+
+        * WebProcess/WebPage/mac/ServicesOverlayController.mm:
+        (WebKit::ServicesOverlayController::ServicesOverlayController):
+        (WebKit::ServicesOverlayController::selectionRectsDidChange):
+        (WebKit::ServicesOverlayController::selectedTelephoneNumberRangesChanged):
+        (WebKit::ServicesOverlayController::clearHighlightState):
+        (WebKit::ServicesOverlayController::drawRect):
+        (WebKit::ServicesOverlayController::drawSelectionHighlight):
+        (WebKit::ServicesOverlayController::maybeDrawTelephoneNumberHighlight):
+        (WebKit::ServicesOverlayController::drawHighlight):
+        (WebKit::ServicesOverlayController::clearSelectionHighlight):
+        (WebKit::ServicesOverlayController::clearHoveredTelephoneNumberHighlight):
+        (WebKit::ServicesOverlayController::establishHoveredTelephoneHighlight): Starts walking the telephone number ranges and
+            creating a highlight for each one that doesn’t already have a highlight. If that highlight is also being hovered by
+            the mouse, then it is set as the hovered telephone number highlight and the method stops creating new highlights.
+        (WebKit::ServicesOverlayController::maybeCreateSelectionHighlight):
+        (WebKit::ServicesOverlayController::mouseEvent):
+        (WebKit::ServicesOverlayController::handleClick):
+        (WebKit::ServicesOverlayController::drawTelephoneNumberHighlight): Deleted.
+        (WebKit::ServicesOverlayController::drawCurrentHighlight): Deleted.
+>>>>>>> Phone number highlights should always be visible if the mouse hovers over.
 
 2014-07-08  Tim Horton  <timothy_horton@apple.com>
 
index f5d984dc438425a971f7b690b33c18eeecf1404f..0e3e1de79f352cc4087f85dbe48d6713a8fb212b 100644 (file)
@@ -51,6 +51,7 @@ namespace WebKit {
     M(TextInput) \
     M(View) \
     M(IDB) \
+    M(Services) \
 
 #define DECLARE_LOG_CHANNEL(name) \
     extern WTFLogChannel JOIN_LOG_CHANNEL_WITH_PREFIX(LOG_CHANNEL_PREFIX, name);
index 06f289ac527ab5de9fcef42dcbc8c564ea47ed1c..e6780a091f9e4ca7a7b275e90bcf75043f648963 100644 (file)
@@ -44,6 +44,17 @@ class WebPage;
 
 typedef void* DDHighlightRef;
 
+struct TelephoneNumberData {
+    TelephoneNumberData(RetainPtr<DDHighlightRef> highlight, PassRefPtr<WebCore::Range> range)
+        : highlight(highlight)
+        , range(range)
+    {
+    }
+
+    RetainPtr<DDHighlightRef> highlight;
+    RefPtr<WebCore::Range> range;
+};
+
 class ServicesOverlayController : private PageOverlay::Client {
 public:
     ServicesOverlayController(WebPage&);
@@ -54,32 +65,39 @@ public:
 
 private:
     void createOverlayIfNeeded();
-    void handleClick(const WebCore::IntPoint&);
+    void handleClick(const WebCore::IntPoint&, DDHighlightRef);
     void clearHighlightState();
-    
+
     virtual void pageOverlayDestroyed(PageOverlay*) override;
     virtual void willMoveToWebPage(PageOverlay*, WebPage*) override;
     virtual void didMoveToWebPage(PageOverlay*, WebPage*) override;
     virtual void drawRect(PageOverlay*, WebCore::GraphicsContext&, const WebCore::IntRect& dirtyRect) override;
     virtual bool mouseEvent(PageOverlay*, const WebMouseEvent&) override;
 
-    void drawTelephoneNumberHighlight(WebCore::GraphicsContext&, const WebCore::IntRect& dirtyRect);
+    bool drawTelephoneNumberHighlightIfVisible(WebCore::GraphicsContext&, const WebCore::IntRect& dirtyRect);
     void drawSelectionHighlight(WebCore::GraphicsContext&, const WebCore::IntRect& dirtyRect);
-    void drawCurrentHighlight(WebCore::GraphicsContext&);
+    void drawHighlight(DDHighlightRef, WebCore::GraphicsContext&);
+
+    void establishHoveredTelephoneHighlight(Boolean& onButton);
+    void maybeCreateSelectionHighlight();
+
+    void clearSelectionHighlight();
+    void clearHoveredTelephoneNumberHighlight();
 
     WebPage* m_webPage;
     PageOverlay* m_servicesOverlay;
     
     Vector<WebCore::LayoutRect> m_currentSelectionRects;
+    RetainPtr<DDHighlightRef> m_selectionHighlight;
+
     Vector<RefPtr<WebCore::Range>> m_currentTelephoneNumberRanges;
+    Vector<RetainPtr<DDHighlightRef>> m_telephoneNumberHighlights;
+    std::unique_ptr<TelephoneNumberData> m_hoveredTelephoneNumberData;
 
-    WebCore::IntPoint m_mousePosition;
-    bool m_mouseIsDownOnButton;
-    bool m_mouseIsOverHighlight;
-    bool m_drawingTelephoneNumberHighlight;
+    RetainPtr<DDHighlightRef> m_currentHoveredHighlight;
+    RetainPtr<DDHighlightRef> m_currentMouseDownOnButtonHighlight;
 
-    RetainPtr<DDHighlightRef> m_currentHighlight;
-    bool m_currentHighlightIsDirty;
+    WebCore::IntPoint m_mousePosition;
 };
 
 } // namespace WebKit
index 9d8cc124ee437b0c6e97f308b69d15cae059c14a..c2a07d021f77fb24c1f0203ba16e112aa5254e3a 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(MAC)
 
+#import "Logging.h"
 #import "WebPage.h"
 #import "WebProcess.h"
 #import <WebCore/Document.h>
@@ -75,10 +76,6 @@ static IntRect textQuadsToBoundingRectForRange(Range& range)
 ServicesOverlayController::ServicesOverlayController(WebPage& webPage)
     : m_webPage(&webPage)
     , m_servicesOverlay(nullptr)
-    , m_mouseIsDownOnButton(false)
-    , m_mouseIsOverHighlight(false)
-    , m_drawingTelephoneNumberHighlight(false)
-    , m_currentHighlightIsDirty(false)
 {
 }
 
@@ -220,7 +217,7 @@ static void compactRectsWithGapRects(Vector<LayoutRect>& rects, const Vector<Gap
 void ServicesOverlayController::selectionRectsDidChange(const Vector<LayoutRect>& rects, const Vector<GapRects>& gapRects)
 {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED > 1090
-    m_currentHighlightIsDirty = true;
+    clearSelectionHighlight();
     m_currentSelectionRects = rects;
 
     compactRectsWithGapRects(m_currentSelectionRects, gapRects);
@@ -228,6 +225,8 @@ void ServicesOverlayController::selectionRectsDidChange(const Vector<LayoutRect>
     // DataDetectors needs these reversed in order to place the arrow in the right location.
     m_currentSelectionRects.reverse();
 
+    LOG(Services, "ServicesOverlayController - Selection rects changed - Now have %lu\n", rects.size());
+
     createOverlayIfNeeded();
 #else
     UNUSED_PARAM(rects);
@@ -237,18 +236,11 @@ void ServicesOverlayController::selectionRectsDidChange(const Vector<LayoutRect>
 void ServicesOverlayController::selectedTelephoneNumberRangesChanged(const Vector<RefPtr<Range>>& ranges)
 {
 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 1090
-    m_currentHighlightIsDirty = true;
+    LOG(Services, "ServicesOverlayController - Telephone number ranges changed - Had %lu, now have %lu\n", m_currentTelephoneNumberRanges.size(), ranges.size());
     m_currentTelephoneNumberRanges = ranges;
-    m_drawingTelephoneNumberHighlight = false;
+    m_telephoneNumberHighlights.clear();
+    m_telephoneNumberHighlights.resize(ranges.size());
 
-    if (ranges.size() == 1) {
-        if (Frame* frame = ranges[0]->startContainer()->document().frame()) {
-            RefPtr<Range> selectionRange = frame->selection().toNormalizedRange();
-            if (ranges[0]->contains(*selectionRange))
-                m_drawingTelephoneNumberHighlight = true;
-        }
-    }
-    
     createOverlayIfNeeded();
 #else
     UNUSED_PARAM(ranges);
@@ -257,11 +249,10 @@ void ServicesOverlayController::selectedTelephoneNumberRangesChanged(const Vecto
 
 void ServicesOverlayController::clearHighlightState()
 {
-    m_mouseIsDownOnButton = false;
-    m_mouseIsOverHighlight = false;
-    m_drawingTelephoneNumberHighlight = false;
+    clearSelectionHighlight();
+    clearHoveredTelephoneNumberHighlight();
 
-    m_currentHighlight = nullptr;
+    m_telephoneNumberHighlights.clear();
 }
 
 void ServicesOverlayController::drawRect(PageOverlay* overlay, WebCore::GraphicsContext& graphicsContext, const WebCore::IntRect& dirtyRect)
@@ -271,16 +262,14 @@ void ServicesOverlayController::drawRect(PageOverlay* overlay, WebCore::Graphics
         return;
     }
 
-    if (m_drawingTelephoneNumberHighlight)
-        drawTelephoneNumberHighlight(graphicsContext, dirtyRect);
-    else
-        drawSelectionHighlight(graphicsContext, dirtyRect);
+    if (drawTelephoneNumberHighlightIfVisible(graphicsContext, dirtyRect))
+        return;
+
+    drawSelectionHighlight(graphicsContext, dirtyRect);
 }
 
 void ServicesOverlayController::drawSelectionHighlight(WebCore::GraphicsContext& graphicsContext, const WebCore::IntRect& dirtyRect)
 {
-    ASSERT(!m_drawingTelephoneNumberHighlight);
-
     // It's possible to end up drawing the selection highlight before we've actually received the selection rects.
     // If that happens we'll end up here again once we have the rects.
     if (m_currentSelectionRects.isEmpty())
@@ -290,75 +279,66 @@ void ServicesOverlayController::drawSelectionHighlight(WebCore::GraphicsContext&
     if (!WebProcess::shared().hasSelectionServices() && m_currentTelephoneNumberRanges.isEmpty())
         return;
 
-    if (!m_currentHighlight || m_currentHighlightIsDirty) {
-        Vector<CGRect> cgRects;
-        cgRects.reserveCapacity(m_currentSelectionRects.size());
-
-        for (auto& rect : m_currentSelectionRects)
-            cgRects.append((CGRect)pixelSnappedIntRect(rect));
+    if (!m_selectionHighlight)
+        maybeCreateSelectionHighlight();
 
-        if (!cgRects.isEmpty()) {
-            CGRect bounds = m_webPage->corePage()->mainFrame().view()->boundsRect();
-            m_currentHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, cgRects.begin(), cgRects.size(), bounds, DDHighlightNoOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
-            m_currentHighlightIsDirty = false;
-        }
-    }
-
-    if (m_currentHighlight)
-        drawCurrentHighlight(graphicsContext);
+    if (m_selectionHighlight)
+        drawHighlight(m_selectionHighlight.get(), graphicsContext);
 }
 
-void ServicesOverlayController::drawTelephoneNumberHighlight(WebCore::GraphicsContext& graphicsContext, const WebCore::IntRect& dirtyRect)
+bool ServicesOverlayController::drawTelephoneNumberHighlightIfVisible(WebCore::GraphicsContext& graphicsContext, const WebCore::IntRect& dirtyRect)
 {
-    ASSERT(m_drawingTelephoneNumberHighlight);
-    ASSERT(m_currentTelephoneNumberRanges.size() == 1);
-
-    auto& range = m_currentTelephoneNumberRanges[0];
-
-    // FIXME: This will choke if the range wraps around the edge of the view.
-    // What should we do in that case?
-    IntRect rect = textQuadsToBoundingRectForRange(*range);
-
-    // Convert to the main document's coordinate space.
-    // FIXME: It's a little crazy to call contentsToWindow and then windowToContents in order to get the right coordinate space.
-    // We should consider adding conversion functions to ScrollView for contentsToDocument(). Right now, contentsToRootView() is
-    // not equivalent to what we need when you have a topContentInset or a header banner.
-    FrameView* viewForRange = range->ownerDocument().view();
-    if (!viewForRange)
-        return;
-    FrameView& mainFrameView = *m_webPage->corePage()->mainFrame().view();
-    rect.setLocation(mainFrameView.windowToContents(viewForRange->contentsToWindow(rect.location())));
+    // Make sure the hovered telephone number highlight is still hovered.
+    if (m_hoveredTelephoneNumberData) {
+        Boolean onButton;
+        if (!DDHighlightPointIsOnHighlight(m_hoveredTelephoneNumberData->highlight.get(), (CGPoint)m_mousePosition, &onButton))
+            clearHoveredTelephoneNumberHighlight();
+
+        bool foundMatchingRange = false;
+
+        // Make sure the hovered highlight still corresponds to a current telephone number range.
+        for (auto& range : m_currentTelephoneNumberRanges) {
+            if (areRangesEqual(range.get(), m_hoveredTelephoneNumberData->range.get())) {
+                foundMatchingRange = true;
+                break;
+            }
+        }
 
-    // If the selection rect is completely outside this drawing tile, don't process it further
-    if (!rect.intersects(dirtyRect))
-        return;
+        if (!foundMatchingRange)
+            clearHoveredTelephoneNumberHighlight();
+    }
 
-    if (!m_currentHighlight || m_currentHighlightIsDirty) {
-        CGRect cgRect = (CGRect)rect;
+    // Found out which - if any - telephone number is hovered.
+    if (!m_hoveredTelephoneNumberData) {
+        Boolean onButton;
+        establishHoveredTelephoneHighlight(onButton);
+    }
 
-        m_currentHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, &cgRect, 1, viewForRange->boundsRect(), DDHighlightOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
-        m_currentHighlightIsDirty = false;
+    // If a telephone number is actually hovered, draw it.
+    if (m_hoveredTelephoneNumberData) {
+        drawHighlight(m_hoveredTelephoneNumberData->highlight.get(), graphicsContext);
+        return true;
     }
 
-    if (m_currentHighlight)
-        drawCurrentHighlight(graphicsContext);
+    return false;
 }
 
-void ServicesOverlayController::drawCurrentHighlight(WebCore::GraphicsContext& graphicsContext)
+void ServicesOverlayController::drawHighlight(DDHighlightRef highlight, WebCore::GraphicsContext& graphicsContext)
 {
-    ASSERT(m_currentHighlight);
+    ASSERT(highlight);
 
     Boolean onButton;
-    m_mouseIsOverHighlight = DDHighlightPointIsOnHighlight(m_currentHighlight.get(), (CGPoint)m_mousePosition, &onButton);
+    bool mouseIsOverHighlight = DDHighlightPointIsOnHighlight(highlight, (CGPoint)m_mousePosition, &onButton);
 
-    // If the mouse is not over the DDHighlight we have no drawing to do.
-    if (!m_mouseIsOverHighlight)
+    if (!mouseIsOverHighlight) {
+        LOG(Services, "ServicesOverlayController::drawHighlight - Mouse is not over highlight, so drawing nothing");
         return;
+    }
 
     CGContextRef cgContext = graphicsContext.platformContext();
     
-    CGLayerRef highlightLayer = DDHighlightGetLayerWithContext(m_currentHighlight.get(), cgContext);
-    CGRect highlightBoundingRect = DDHighlightGetBoundingRect(m_currentHighlight.get());
+    CGLayerRef highlightLayer = DDHighlightGetLayerWithContext(highlight, cgContext);
+    CGRect highlightBoundingRect = DDHighlightGetBoundingRect(highlight);
     
     GraphicsContextStateSaver stateSaver(graphicsContext);
 
@@ -371,55 +351,144 @@ void ServicesOverlayController::drawCurrentHighlight(WebCore::GraphicsContext& g
     CGContextDrawLayerInRect(cgContext, highlightDrawRect, highlightLayer);
 }
 
+void ServicesOverlayController::clearSelectionHighlight()
+{
+    if (!m_selectionHighlight)
+        return;
+
+    if (m_currentHoveredHighlight == m_selectionHighlight)
+        m_currentHoveredHighlight = nullptr;
+    if (m_currentMouseDownOnButtonHighlight == m_selectionHighlight)
+        m_currentMouseDownOnButtonHighlight = nullptr;
+    m_selectionHighlight = nullptr;
+}
+
+void ServicesOverlayController::clearHoveredTelephoneNumberHighlight()
+{
+    if (!m_hoveredTelephoneNumberData)
+        return;
+
+    if (m_currentHoveredHighlight == m_hoveredTelephoneNumberData->highlight)
+        m_currentHoveredHighlight = nullptr;
+    if (m_currentMouseDownOnButtonHighlight == m_hoveredTelephoneNumberData->highlight)
+        m_currentMouseDownOnButtonHighlight = nullptr;
+    m_hoveredTelephoneNumberData = nullptr;
+}
+
+void ServicesOverlayController::establishHoveredTelephoneHighlight(Boolean& onButton)
+{
+    ASSERT(m_currentTelephoneNumberRanges.size() == m_telephoneNumberHighlights.size());
+
+    for (unsigned i = 0; i < m_currentTelephoneNumberRanges.size(); ++i) {
+        if (!m_telephoneNumberHighlights[i]) {
+            // FIXME: This will choke if the range wraps around the edge of the view.
+            // What should we do in that case?
+            IntRect rect = textQuadsToBoundingRectForRange(*m_currentTelephoneNumberRanges[i]);
+
+            // Convert to the main document's coordinate space.
+            // FIXME: It's a little crazy to call contentsToWindow and then windowToContents in order to get the right coordinate space.
+            // We should consider adding conversion functions to ScrollView for contentsToDocument(). Right now, contentsToRootView() is
+            // not equivalent to what we need when you have a topContentInset or a header banner.
+            FrameView* viewForRange = m_currentTelephoneNumberRanges[i]->ownerDocument().view();
+            if (!viewForRange)
+                continue;
+            FrameView& mainFrameView = *m_webPage->corePage()->mainFrame().view();
+            rect.setLocation(mainFrameView.windowToContents(viewForRange->contentsToWindow(rect.location())));
+
+            CGRect cgRect = rect;
+            m_telephoneNumberHighlights[i] = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, &cgRect, 1, viewForRange->boundsRect(), DDHighlightOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
+        }
+
+        if (!DDHighlightPointIsOnHighlight(m_telephoneNumberHighlights[i].get(), (CGPoint)m_mousePosition, &onButton))
+            continue;
+
+        if (!m_hoveredTelephoneNumberData || m_hoveredTelephoneNumberData->highlight != m_telephoneNumberHighlights[i])
+            m_hoveredTelephoneNumberData = std::make_unique<TelephoneNumberData>(m_telephoneNumberHighlights[i], m_currentTelephoneNumberRanges[i]);
+
+        m_servicesOverlay->setNeedsDisplay();
+        return;
+    }
+
+    clearHoveredTelephoneNumberHighlight();
+    onButton = false;
+}
+
+void ServicesOverlayController::maybeCreateSelectionHighlight()
+{
+    ASSERT(!m_selectionHighlight);
+    ASSERT(m_servicesOverlay);
+
+    Vector<CGRect> cgRects;
+    cgRects.reserveCapacity(m_currentSelectionRects.size());
+
+    for (auto& rect : m_currentSelectionRects)
+        cgRects.append((CGRect)pixelSnappedIntRect(rect));
+
+    if (!cgRects.isEmpty()) {
+        CGRect bounds = m_webPage->corePage()->mainFrame().view()->boundsRect();
+        m_selectionHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, cgRects.begin(), cgRects.size(), bounds, DDHighlightNoOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
+
+        m_servicesOverlay->setNeedsDisplay();
+    }
+}
+
 bool ServicesOverlayController::mouseEvent(PageOverlay*, const WebMouseEvent& event)
 {
     m_mousePosition = m_webPage->corePage()->mainFrame().view()->rootViewToContents(event.position());
 
-    bool mouseWasOverHighlight = m_mouseIsOverHighlight;
+    DDHighlightRef oldHoveredHighlight = m_currentHoveredHighlight.get();
+
     Boolean onButton = false;
-    m_mouseIsOverHighlight = m_currentHighlight ? DDHighlightPointIsOnHighlight(m_currentHighlight.get(), (CGPoint)m_mousePosition, &onButton) : false;
+    establishHoveredTelephoneHighlight(onButton);
+    if (m_hoveredTelephoneNumberData) {
+        ASSERT(m_hoveredTelephoneNumberData->highlight);
+        m_currentHoveredHighlight = m_hoveredTelephoneNumberData->highlight;
+    } else {
+        if (!m_selectionHighlight)
+            maybeCreateSelectionHighlight();
 
-    if (mouseWasOverHighlight != m_mouseIsOverHighlight)
+        if (m_selectionHighlight && DDHighlightPointIsOnHighlight(m_selectionHighlight.get(), (CGPoint)m_mousePosition, &onButton))
+            m_currentHoveredHighlight = m_selectionHighlight;
+        else
+            m_currentHoveredHighlight = nullptr;
+    }
+
+    if (oldHoveredHighlight != m_currentHoveredHighlight)
         m_servicesOverlay->setNeedsDisplay();
 
     // If this event has nothing to do with the left button, it clears the current mouse down tracking and we're done processing it.
     if (event.button() != WebMouseEvent::LeftButton) {
-        m_mouseIsDownOnButton = false;
+        m_currentMouseDownOnButtonHighlight = nullptr;
         return false;
     }
 
-    if (!m_currentHighlight)
-        return false;
-
     // Check and see if the mouse went up and we have a current mouse down highlight button.
     if (event.type() == WebEvent::MouseUp) {
-        bool mouseWasDownOnButton = m_mouseIsDownOnButton;
-        m_mouseIsDownOnButton = false;
+        RetainPtr<DDHighlightRef> mouseDownHighlight = std::move(m_currentMouseDownOnButtonHighlight);
 
         // If the mouse lifted while still over the highlight button that it went down on, then that is a click.
-        if (m_mouseIsOverHighlight && onButton && mouseWasDownOnButton) {
-            handleClick(m_mousePosition);
+        if (onButton && mouseDownHighlight) {
+            handleClick(m_mousePosition, mouseDownHighlight.get());
             return true;
         }
         
         return false;
     }
-    
+
     // Check and see if the mouse moved within the confines of the DD highlight button.
     if (event.type() == WebEvent::MouseMove) {
         // Moving with the mouse button down is okay as long as the mouse never leaves the highlight button.
-        if (m_mouseIsOverHighlight && onButton)
+        if (m_currentMouseDownOnButtonHighlight && onButton)
             return true;
 
-        m_mouseIsDownOnButton = false;
-        
+        m_currentMouseDownOnButtonHighlight = nullptr;
         return false;
     }
-    
+
     // Check and see if the mouse went down over a DD highlight button.
     if (event.type() == WebEvent::MouseDown) {
-        if (m_mouseIsOverHighlight && onButton) {
-            m_mouseIsDownOnButton = true;
+        if (m_currentHoveredHighlight && onButton) {
+            m_currentMouseDownOnButtonHighlight = m_currentHoveredHighlight;
             m_servicesOverlay->setNeedsDisplay();
             return true;
         }
@@ -430,25 +499,27 @@ bool ServicesOverlayController::mouseEvent(PageOverlay*, const WebMouseEvent& ev
     return false;
 }
 
-void ServicesOverlayController::handleClick(const WebCore::IntPoint& clickPoint)
+void ServicesOverlayController::handleClick(const WebCore::IntPoint& clickPoint, DDHighlightRef highlight)
 {
+    ASSERT(highlight);
+
     FrameView* frameView = m_webPage->mainFrameView();
     if (!frameView)
         return;
 
     IntPoint windowPoint = frameView->contentsToWindow(clickPoint);
 
-    if (m_drawingTelephoneNumberHighlight) {
-        ASSERT(m_currentTelephoneNumberRanges.size() == 1);
-        m_webPage->handleTelephoneNumberClick(m_currentTelephoneNumberRanges[0]->text(), windowPoint);
-    } else {
+    if (highlight == m_selectionHighlight) {
         Vector<String> selectedTelephoneNumbers;
         selectedTelephoneNumbers.reserveCapacity(m_currentTelephoneNumberRanges.size());
         for (auto& range : m_currentTelephoneNumberRanges)
             selectedTelephoneNumbers.append(range->text());
 
         m_webPage->handleSelectionServiceClick(m_webPage->corePage()->mainFrame().selection(), selectedTelephoneNumbers, windowPoint);
-    }
+    } else if (m_hoveredTelephoneNumberData && m_hoveredTelephoneNumberData->highlight == highlight)
+        m_webPage->handleTelephoneNumberClick(m_hoveredTelephoneNumberData->range->text(), windowPoint);
+    else
+        ASSERT_NOT_REACHED();
 }
     
 } // namespace WebKit