<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[172382] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/172382">172382</a></dd>
<dt>Author</dt> <dd>timothy_horton@apple.com</dd>
<dt>Date</dt> <dd>2014-08-10 18:23:36 -0700 (Sun, 10 Aug 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Refactor ServiceOverlayController in preparation for fading between highlights
https://bugs.webkit.org/show_bug.cgi?id=135787
&lt;rdar://problem/17935736&gt;

Reviewed by Brady Eidson.

Rework ServicesOverlayController so that we always keep a set of generic
&quot;potential highlights&quot;, which are refcounted Highlight objects and
wrap a DDHighlightRef, as well as a type (Selection or TelephoneNumber),
Range (only used in the case of TelephoneNumber), and potentially more
things in the future (like, say, fade state!).

We eagerly update the list of potential highlights when the selection or set
of detected telephone numbers changes, and use this information to install
or uninstall the page overlay as needed.

When we need to recompute the &quot;active&quot; highlight from this set (for example,
we need to handle a mouse event or paint the highlight), we look through
the set of potential highlights and decide. This moves the &quot;active&quot; highlight
decision logic into one small and confined place.

* WebProcess/WebPage/ServicesOverlayController.h:
(WebKit::ServicesOverlayController::Highlight):
Add the new aforementioned refcounted Highlight class.
Rename m_lastHoveredHighlightChangeTime to m_lastActiveHighlightChangeTime.
Make m_webPage a reference.
The rest is just added/removed/adjusted functions for the refactoring.

(WebKit::TelephoneNumberData::TelephoneNumberData): Deleted.
* WebProcess/WebPage/mac/ServicesOverlayController.mm:
(WebKit::ServicesOverlayController::Highlight::createForSelection):
(WebKit::ServicesOverlayController::Highlight::createForTelephoneNumber):
Create Highlights for the two different highlight types.

(WebKit::ServicesOverlayController::ServicesOverlayController):
(WebKit::ServicesOverlayController::willMoveToWebPage):
Our WebPage pointer is always valid because it owns us; don't clear it.
We need to keep it around so that we can uninstall the overlay and
install it again later, anyway.

(WebKit::ServicesOverlayController::selectionRectsDidChange):
(WebKit::ServicesOverlayController::selectedTelephoneNumberRangesChanged):
When selection rects or detected telephone numbers change, rebuild potential highlights.
This will have the side-effect of installing the overlay if needed.

(WebKit::ServicesOverlayController::mouseIsOverHighlight):
Make this function take a Highlight instead of a DDHighlightRef.

(WebKit::ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown):
Make this function take a Highlight instead of a DDHighlightRef.

(WebKit::ServicesOverlayController::drawHighlight):
Make this function take a Highlight instead of a DDHighlightRef.
There's no reason to do the translation separately from the layer blit,
also allowing us to avoid the StateSaver.

(WebKit::ServicesOverlayController::drawRect):
drawRect now always paints the active highlight, instead of duplicating
logic about which highlight should be active.
Also, it will update the active highlight before painting.
We no longer need to re-determine whether the active highlight's phone
number range is still a valid phone number range, because we rebuild
the potential highlights whenever the set of phone number ranges changes.

(WebKit::ServicesOverlayController::clearActiveHighlight):
Mostly an adoption of new names.

(WebKit::ServicesOverlayController::removeAllPotentialHighlightsOfType):
Run through the list of potential highlights and remove any of the given type.
The two highlight building functions use this helper to clear the old ones before building.

(WebKit::ServicesOverlayController::buildPhoneNumberHighlights):
(WebKit::ServicesOverlayController::buildSelectionHighlight):
Rebuild the list of potential highlights, replacing all highlights of
the given type with new ones.

(WebKit::ServicesOverlayController::hasRelevantSelectionServices):
Factor out the code that decides whether our current selection is
viable for servicing based on whether we have plain-text and/or rich-text services.

(WebKit::ServicesOverlayController::didRebuildPotentialHighlights):
When rebuilding potential highlights, if we have no potential highlights at all,
uninstall the page overlay; we don't need mouse tracking and don't need to
paint anything. This improves memory use and compositing performance significantly,
where previously we were leaving the overlay up forever after creating it.

If we have either detected telephone numbers or relevant selection services,
create and install the overlay if it doesn't already exist.

(WebKit::ServicesOverlayController::createOverlayIfNeeded):
This just moved from elsehwere, except that it now uses FadeMode::DoNotFade.
It doesn't make sense to fade on install/uninstall (which happens even before hover)
but not on changing the active highlight; fading will be re-addressed in the next patch.

(WebKit::ServicesOverlayController::highlightsAreEquivalent):
Determine whether two highlights are equivalent. While we may have
created a new Highlight at rebuild time, if two telephone number
highlights have equivalent ranges, there's no need to 'transition' to the new one.

(WebKit::ServicesOverlayController::determineActiveHighlight):
Run through the list of services, and try to find one that is hovered.
We prefer telephone number highlights to selection highlights, and
we will never make a selection highlight active if it is both
not serviceable and there are no telephone numbers to show in the combined menu.
This is the centralized location for determination of which highlight
should be considered active. If the active highlight changed, update
the time since last change and cancel the mouse-down tracking.

(WebKit::ServicesOverlayController::mouseEvent):
Adjust some comments to be more explanatory.
A bunch of code moved out of here and into determineActiveHighlight.

(WebKit::ServicesOverlayController::handleClick):
Adjust to take a reference and use Highlight instead of DDHighlightRef.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageServicesOverlayControllerh">trunk/Source/WebKit2/WebProcess/WebPage/ServicesOverlayController.h</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPagemacServicesOverlayControllermm">trunk/Source/WebKit2/WebProcess/WebPage/mac/ServicesOverlayController.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (172381 => 172382)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-08-10 20:12:01 UTC (rev 172381)
+++ trunk/Source/WebKit2/ChangeLog        2014-08-11 01:23:36 UTC (rev 172382)
</span><span class="lines">@@ -1,3 +1,120 @@
</span><ins>+2014-08-10  Tim Horton  &lt;timothy_horton@apple.com&gt;
+
+        Refactor ServiceOverlayController in preparation for fading between highlights
+        https://bugs.webkit.org/show_bug.cgi?id=135787
+        &lt;rdar://problem/17935736&gt;
+
+        Reviewed by Brady Eidson.
+
+        Rework ServicesOverlayController so that we always keep a set of generic
+        &quot;potential highlights&quot;, which are refcounted Highlight objects and
+        wrap a DDHighlightRef, as well as a type (Selection or TelephoneNumber),
+        Range (only used in the case of TelephoneNumber), and potentially more
+        things in the future (like, say, fade state!).
+
+        We eagerly update the list of potential highlights when the selection or set
+        of detected telephone numbers changes, and use this information to install
+        or uninstall the page overlay as needed.
+
+        When we need to recompute the &quot;active&quot; highlight from this set (for example,
+        we need to handle a mouse event or paint the highlight), we look through
+        the set of potential highlights and decide. This moves the &quot;active&quot; highlight
+        decision logic into one small and confined place.
+
+        * WebProcess/WebPage/ServicesOverlayController.h:
+        (WebKit::ServicesOverlayController::Highlight):
+        Add the new aforementioned refcounted Highlight class.
+        Rename m_lastHoveredHighlightChangeTime to m_lastActiveHighlightChangeTime.
+        Make m_webPage a reference.
+        The rest is just added/removed/adjusted functions for the refactoring.
+
+        (WebKit::TelephoneNumberData::TelephoneNumberData): Deleted.
+        * WebProcess/WebPage/mac/ServicesOverlayController.mm:
+        (WebKit::ServicesOverlayController::Highlight::createForSelection):
+        (WebKit::ServicesOverlayController::Highlight::createForTelephoneNumber):
+        Create Highlights for the two different highlight types.
+
+        (WebKit::ServicesOverlayController::ServicesOverlayController):
+        (WebKit::ServicesOverlayController::willMoveToWebPage):
+        Our WebPage pointer is always valid because it owns us; don't clear it.
+        We need to keep it around so that we can uninstall the overlay and
+        install it again later, anyway.
+
+        (WebKit::ServicesOverlayController::selectionRectsDidChange):
+        (WebKit::ServicesOverlayController::selectedTelephoneNumberRangesChanged):
+        When selection rects or detected telephone numbers change, rebuild potential highlights.
+        This will have the side-effect of installing the overlay if needed.
+
+        (WebKit::ServicesOverlayController::mouseIsOverHighlight):
+        Make this function take a Highlight instead of a DDHighlightRef.
+
+        (WebKit::ServicesOverlayController::remainingTimeUntilHighlightShouldBeShown):
+        Make this function take a Highlight instead of a DDHighlightRef.
+
+        (WebKit::ServicesOverlayController::drawHighlight):
+        Make this function take a Highlight instead of a DDHighlightRef.
+        There's no reason to do the translation separately from the layer blit,
+        also allowing us to avoid the StateSaver.
+
+        (WebKit::ServicesOverlayController::drawRect):
+        drawRect now always paints the active highlight, instead of duplicating
+        logic about which highlight should be active.
+        Also, it will update the active highlight before painting.
+        We no longer need to re-determine whether the active highlight's phone
+        number range is still a valid phone number range, because we rebuild
+        the potential highlights whenever the set of phone number ranges changes.
+
+        (WebKit::ServicesOverlayController::clearActiveHighlight):
+        Mostly an adoption of new names.
+
+        (WebKit::ServicesOverlayController::removeAllPotentialHighlightsOfType):
+        Run through the list of potential highlights and remove any of the given type.
+        The two highlight building functions use this helper to clear the old ones before building.
+
+        (WebKit::ServicesOverlayController::buildPhoneNumberHighlights):
+        (WebKit::ServicesOverlayController::buildSelectionHighlight):
+        Rebuild the list of potential highlights, replacing all highlights of
+        the given type with new ones.
+
+        (WebKit::ServicesOverlayController::hasRelevantSelectionServices):
+        Factor out the code that decides whether our current selection is
+        viable for servicing based on whether we have plain-text and/or rich-text services.
+
+        (WebKit::ServicesOverlayController::didRebuildPotentialHighlights):
+        When rebuilding potential highlights, if we have no potential highlights at all,
+        uninstall the page overlay; we don't need mouse tracking and don't need to
+        paint anything. This improves memory use and compositing performance significantly,
+        where previously we were leaving the overlay up forever after creating it.
+
+        If we have either detected telephone numbers or relevant selection services,
+        create and install the overlay if it doesn't already exist.
+
+        (WebKit::ServicesOverlayController::createOverlayIfNeeded):
+        This just moved from elsehwere, except that it now uses FadeMode::DoNotFade.
+        It doesn't make sense to fade on install/uninstall (which happens even before hover)
+        but not on changing the active highlight; fading will be re-addressed in the next patch.
+
+        (WebKit::ServicesOverlayController::highlightsAreEquivalent):
+        Determine whether two highlights are equivalent. While we may have
+        created a new Highlight at rebuild time, if two telephone number
+        highlights have equivalent ranges, there's no need to 'transition' to the new one.
+
+        (WebKit::ServicesOverlayController::determineActiveHighlight):
+        Run through the list of services, and try to find one that is hovered.
+        We prefer telephone number highlights to selection highlights, and
+        we will never make a selection highlight active if it is both
+        not serviceable and there are no telephone numbers to show in the combined menu.
+        This is the centralized location for determination of which highlight
+        should be considered active. If the active highlight changed, update
+        the time since last change and cancel the mouse-down tracking.
+
+        (WebKit::ServicesOverlayController::mouseEvent):
+        Adjust some comments to be more explanatory.
+        A bunch of code moved out of here and into determineActiveHighlight.
+
+        (WebKit::ServicesOverlayController::handleClick):
+        Adjust to take a reference and use Highlight instead of DDHighlightRef.
+
</ins><span class="cx"> 2014-08-10  Timothy Hatcher  &lt;timothy@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: new glyphs are visible on OS X 10.9 builds
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageServicesOverlayControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/ServicesOverlayController.h (172381 => 172382)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/ServicesOverlayController.h        2014-08-10 20:12:01 UTC (rev 172381)
+++ trunk/Source/WebKit2/WebProcess/WebPage/ServicesOverlayController.h        2014-08-11 01:23:36 UTC (rev 172382)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> #include &quot;PageOverlay.h&quot;
</span><span class="cx"> #include &lt;WebCore/Range.h&gt;
</span><span class="cx"> #include &lt;WebCore/Timer.h&gt;
</span><ins>+#include &lt;wtf/RefCounted.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> typedef void* DDHighlightRef;
</span><span class="cx"> 
</span><span class="lines">@@ -44,19 +45,6 @@
</span><span class="cx"> 
</span><span class="cx"> class WebPage;
</span><span class="cx"> 
</span><del>-typedef void* DDHighlightRef;
-
-struct TelephoneNumberData {
-    TelephoneNumberData(RetainPtr&lt;DDHighlightRef&gt; highlight, PassRefPtr&lt;WebCore::Range&gt; range)
-        : highlight(highlight)
-        , range(range)
-    {
-    }
-
-    RetainPtr&lt;DDHighlightRef&gt; highlight;
-    RefPtr&lt;WebCore::Range&gt; range;
-};
-
</del><span class="cx"> class ServicesOverlayController : private PageOverlay::Client {
</span><span class="cx"> public:
</span><span class="cx">     ServicesOverlayController(WebPage&amp;);
</span><span class="lines">@@ -66,49 +54,81 @@
</span><span class="cx">     void selectionRectsDidChange(const Vector&lt;WebCore::LayoutRect&gt;&amp;, const Vector&lt;WebCore::GapRects&gt;&amp;, bool isTextOnly);
</span><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    void createOverlayIfNeeded();
-    void handleClick(const WebCore::IntPoint&amp;, DDHighlightRef);
-    void clearHighlightState();
</del><ins>+    class Highlight : public RefCounted&lt;Highlight&gt; {
+        WTF_MAKE_NONCOPYABLE(Highlight);
+    public:
+        static PassRefPtr&lt;Highlight&gt; createForSelection(RetainPtr&lt;DDHighlightRef&gt;);
+        static PassRefPtr&lt;Highlight&gt; createForTelephoneNumber(RetainPtr&lt;DDHighlightRef&gt;, PassRefPtr&lt;WebCore::Range&gt;);
</ins><span class="cx"> 
</span><ins>+        DDHighlightRef ddHighlight() const { return m_ddHighlight.get(); }
+        WebCore::Range* range() const { return m_range.get(); }
+
+        enum class Type {
+            TelephoneNumber,
+            Selection
+        };
+        Type type() const { return m_type; }
+
+    private:
+        explicit Highlight(Type type, RetainPtr&lt;DDHighlightRef&gt; ddHighlight, PassRefPtr&lt;WebCore::Range&gt; range)
+            : m_ddHighlight(ddHighlight)
+            , m_range(range)
+            , m_type(type)
+        {
+            ASSERT(m_ddHighlight);
+            ASSERT(type != Type::TelephoneNumber || m_range);
+        }
+
+        RetainPtr&lt;DDHighlightRef&gt; m_ddHighlight;
+        RefPtr&lt;WebCore::Range&gt; m_range;
+        Type m_type;
+    };
+
+    // PageOverlay::Client
</ins><span class="cx">     virtual void pageOverlayDestroyed(PageOverlay*) override;
</span><span class="cx">     virtual void willMoveToWebPage(PageOverlay*, WebPage*) override;
</span><span class="cx">     virtual void didMoveToWebPage(PageOverlay*, WebPage*) override;
</span><span class="cx">     virtual void drawRect(PageOverlay*, WebCore::GraphicsContext&amp;, const WebCore::IntRect&amp; dirtyRect) override;
</span><span class="cx">     virtual bool mouseEvent(PageOverlay*, const WebMouseEvent&amp;) override;
</span><span class="cx"> 
</span><del>-    bool drawTelephoneNumberHighlightIfVisible(WebCore::GraphicsContext&amp;, const WebCore::IntRect&amp; dirtyRect);
-    void drawSelectionHighlight(WebCore::GraphicsContext&amp;, const WebCore::IntRect&amp; dirtyRect);
-    void drawHighlight(DDHighlightRef, WebCore::GraphicsContext&amp;);
</del><ins>+    void createOverlayIfNeeded();
+    void handleClick(const WebCore::IntPoint&amp;, Highlight&amp;);
</ins><span class="cx"> 
</span><del>-    void establishHoveredTelephoneHighlight(bool&amp; mouseIsOverButton);
-    void maybeCreateSelectionHighlight();
</del><ins>+    void drawHighlight(Highlight&amp;, WebCore::GraphicsContext&amp;);
</ins><span class="cx"> 
</span><del>-    void clearSelectionHighlight();
-    void clearHoveredTelephoneNumberHighlight();
</del><ins>+    void removeAllPotentialHighlightsOfType(Highlight::Type);
+    void buildPhoneNumberHighlights();
+    void buildSelectionHighlight();
+    void didRebuildPotentialHighlights();
</ins><span class="cx"> 
</span><del>-    bool mouseIsOverHighlight(DDHighlightRef, bool&amp; mouseIsOverButton) const;
</del><ins>+    void determineActiveHighlight(bool&amp; mouseIsOverButton);
+    void clearActiveHighlight();
+
+    bool hasRelevantSelectionServices();
+
+    bool mouseIsOverHighlight(Highlight&amp;, bool&amp; mouseIsOverButton) const;
</ins><span class="cx">     std::chrono::milliseconds remainingTimeUntilHighlightShouldBeShown() const;
</span><span class="cx">     void repaintHighlightTimerFired(WebCore::Timer&lt;ServicesOverlayController&gt;&amp;);
</span><span class="cx"> 
</span><del>-    WebPage* m_webPage;
</del><ins>+    static bool highlightsAreEquivalent(const Highlight* a, const Highlight* b);
+
+    WebPage&amp; m_webPage;
</ins><span class="cx">     PageOverlay* m_servicesOverlay;
</span><del>-    
</del><ins>+
+    RefPtr&lt;Highlight&gt; m_activeHighlight;
+    HashSet&lt;RefPtr&lt;Highlight&gt;&gt; m_potentialHighlights;
+
+    Vector&lt;RefPtr&lt;WebCore::Range&gt;&gt; m_currentTelephoneNumberRanges;
</ins><span class="cx">     Vector&lt;WebCore::LayoutRect&gt; m_currentSelectionRects;
</span><del>-    RetainPtr&lt;DDHighlightRef&gt; m_selectionHighlight;
</del><span class="cx">     bool m_isTextOnly;
</span><ins>+
</ins><span class="cx">     std::chrono::steady_clock::time_point m_lastSelectionChangeTime;
</span><del>-    std::chrono::steady_clock::time_point m_lastHoveredHighlightChangeTime;
</del><ins>+    std::chrono::steady_clock::time_point m_lastActiveHighlightChangeTime;
</ins><span class="cx"> 
</span><del>-    Vector&lt;RefPtr&lt;WebCore::Range&gt;&gt; m_currentTelephoneNumberRanges;
-    Vector&lt;RetainPtr&lt;DDHighlightRef&gt;&gt; m_telephoneNumberHighlights;
-    std::unique_ptr&lt;TelephoneNumberData&gt; m_hoveredTelephoneNumberData;
</del><ins>+    RefPtr&lt;Highlight&gt; m_currentMouseDownOnButtonHighlight;
+    WebCore::IntPoint m_mousePosition;
</ins><span class="cx"> 
</span><del>-    RetainPtr&lt;DDHighlightRef&gt; m_currentHoveredHighlight;
-    RetainPtr&lt;DDHighlightRef&gt; m_currentMouseDownOnButtonHighlight;
-
</del><span class="cx">     WebCore::Timer&lt;ServicesOverlayController&gt; m_repaintHighlightTimer;
</span><del>-
-    WebCore::IntPoint m_mousePosition;
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPagemacServicesOverlayControllermm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/mac/ServicesOverlayController.mm (172381 => 172382)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/mac/ServicesOverlayController.mm        2014-08-10 20:12:01 UTC (rev 172381)
+++ trunk/Source/WebKit2/WebProcess/WebPage/mac/ServicesOverlayController.mm        2014-08-11 01:23:36 UTC (rev 172382)
</span><span class="lines">@@ -63,6 +63,16 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><ins>+PassRefPtr&lt;ServicesOverlayController::Highlight&gt; ServicesOverlayController::Highlight::createForSelection(RetainPtr&lt;DDHighlightRef&gt; ddHighlight)
+{
+    return adoptRef(new Highlight(Type::Selection, ddHighlight, nullptr));
+}
+
+PassRefPtr&lt;ServicesOverlayController::Highlight&gt; ServicesOverlayController::Highlight::createForTelephoneNumber(RetainPtr&lt;DDHighlightRef&gt; ddHighlight, PassRefPtr&lt;Range&gt; range)
+{
+    return adoptRef(new Highlight(Type::TelephoneNumber, ddHighlight, range));
+}
+
</ins><span class="cx"> static IntRect textQuadsToBoundingRectForRange(Range&amp; range)
</span><span class="cx"> {
</span><span class="cx">     Vector&lt;FloatQuad&gt; textQuads;
</span><span class="lines">@@ -74,7 +84,7 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> ServicesOverlayController::ServicesOverlayController(WebPage&amp; webPage)
</span><del>-    : m_webPage(&amp;webPage)
</del><ins>+    : m_webPage(webPage)
</ins><span class="cx">     , m_servicesOverlay(nullptr)
</span><span class="cx">     , m_isTextOnly(false)
</span><span class="cx">     , m_repaintHighlightTimer(this, &amp;ServicesOverlayController::repaintHighlightTimerFired)
</span><span class="lines">@@ -99,31 +109,12 @@
</span><span class="cx"> 
</span><span class="cx">     ASSERT(m_servicesOverlay);
</span><span class="cx">     m_servicesOverlay = nullptr;
</span><del>-
-    ASSERT(m_webPage);
-    m_webPage = nullptr;
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ServicesOverlayController::didMoveToWebPage(PageOverlay*, WebPage*)
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ServicesOverlayController::createOverlayIfNeeded()
-{
-    if (m_servicesOverlay) {
-        m_servicesOverlay-&gt;setNeedsDisplay();
-        return;
-    }
-
-    if (m_currentTelephoneNumberRanges.isEmpty() &amp;&amp; (!WebProcess::shared().hasSelectionServices() || m_currentSelectionRects.isEmpty()))
-        return;
-
-    RefPtr&lt;PageOverlay&gt; overlay = PageOverlay::create(this, PageOverlay::OverlayType::Document);
-    m_servicesOverlay = overlay.get();
-    m_webPage-&gt;installPageOverlay(overlay.release(), PageOverlay::FadeMode::Fade);
-    m_servicesOverlay-&gt;setNeedsDisplay();
-}
-
</del><span class="cx"> static const uint8_t AlignmentNone = 0;
</span><span class="cx"> static const uint8_t AlignmentLeft = 1 &lt;&lt; 0;
</span><span class="cx"> static const uint8_t AlignmentRight = 1 &lt;&lt; 1;
</span><span class="lines">@@ -215,7 +206,6 @@
</span><span class="cx"> void ServicesOverlayController::selectionRectsDidChange(const Vector&lt;LayoutRect&gt;&amp; rects, const Vector&lt;GapRects&gt;&amp; gapRects, bool isTextOnly)
</span><span class="cx"> {
</span><span class="cx"> #if __MAC_OS_X_VERSION_MIN_REQUIRED &gt; 1090
</span><del>-    clearSelectionHighlight();
</del><span class="cx">     m_currentSelectionRects = rects;
</span><span class="cx">     m_isTextOnly = isTextOnly;
</span><span class="cx"> 
</span><span class="lines">@@ -228,7 +218,7 @@
</span><span class="cx"> 
</span><span class="cx">     LOG(Services, &quot;ServicesOverlayController - Selection rects changed - Now have %lu\n&quot;, rects.size());
</span><span class="cx"> 
</span><del>-    createOverlayIfNeeded();
</del><ins>+    buildSelectionHighlight();
</ins><span class="cx"> #else
</span><span class="cx">     UNUSED_PARAM(rects);
</span><span class="cx"> #endif
</span><span class="lines">@@ -239,95 +229,17 @@
</span><span class="cx"> #if PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &gt; 1090
</span><span class="cx">     LOG(Services, &quot;ServicesOverlayController - Telephone number ranges changed - Had %lu, now have %lu\n&quot;, m_currentTelephoneNumberRanges.size(), ranges.size());
</span><span class="cx">     m_currentTelephoneNumberRanges = ranges;
</span><del>-    m_telephoneNumberHighlights.clear();
-    m_telephoneNumberHighlights.resize(ranges.size());
</del><span class="cx"> 
</span><del>-    createOverlayIfNeeded();
</del><ins>+    buildPhoneNumberHighlights();
</ins><span class="cx"> #else
</span><span class="cx">     UNUSED_PARAM(ranges);
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ServicesOverlayController::clearHighlightState()
</del><ins>+bool ServicesOverlayController::mouseIsOverHighlight(Highlight&amp; highlight, bool&amp; mouseIsOverButton) const
</ins><span class="cx"> {
</span><del>-    clearSelectionHighlight();
-    clearHoveredTelephoneNumberHighlight();
-
-    m_telephoneNumberHighlights.clear();
-}
-
-void ServicesOverlayController::drawRect(PageOverlay* overlay, WebCore::GraphicsContext&amp; graphicsContext, const WebCore::IntRect&amp; dirtyRect)
-{
-    if (m_currentSelectionRects.isEmpty() &amp;&amp; m_currentTelephoneNumberRanges.isEmpty()) {
-        clearHighlightState();
-        return;
-    }
-
-    if (drawTelephoneNumberHighlightIfVisible(graphicsContext, dirtyRect))
-        return;
-
-    drawSelectionHighlight(graphicsContext, dirtyRect);
-}
-
-void ServicesOverlayController::drawSelectionHighlight(WebCore::GraphicsContext&amp; graphicsContext, const WebCore::IntRect&amp; dirtyRect)
-{
-    // 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())
-        return;
-
-    // If there are no appropriate installed selection services and we have no phone numbers detected, then we have nothing to draw.
-    if ((!WebProcess::shared().hasSelectionServices() || (!WebProcess::shared().hasRichContentServices() &amp;&amp; !m_isTextOnly)) &amp;&amp; m_currentTelephoneNumberRanges.isEmpty())
-        return;
-
-    if (!m_selectionHighlight)
-        maybeCreateSelectionHighlight();
-
-    if (m_selectionHighlight)
-        drawHighlight(m_selectionHighlight.get(), graphicsContext);
-}
-
-bool ServicesOverlayController::drawTelephoneNumberHighlightIfVisible(WebCore::GraphicsContext&amp; graphicsContext, const WebCore::IntRect&amp; dirtyRect)
-{
-    // Make sure the hovered telephone number highlight is still hovered.
-    if (m_hoveredTelephoneNumberData) {
-        Boolean onButton;
-        if (!DDHighlightPointIsOnHighlight(m_hoveredTelephoneNumberData-&gt;highlight.get(), (CGPoint)m_mousePosition, &amp;onButton))
-            clearHoveredTelephoneNumberHighlight();
-
-        bool foundMatchingRange = false;
-
-        // Make sure the hovered highlight still corresponds to a current telephone number range.
-        for (auto&amp; range : m_currentTelephoneNumberRanges) {
-            if (areRangesEqual(range.get(), m_hoveredTelephoneNumberData-&gt;range.get())) {
-                foundMatchingRange = true;
-                break;
-            }
-        }
-
-        if (!foundMatchingRange)
-            clearHoveredTelephoneNumberHighlight();
-    }
-
-    // Found out which - if any - telephone number is hovered.
-    if (!m_hoveredTelephoneNumberData) {
-        bool mouseIsOverButton;
-        establishHoveredTelephoneHighlight(mouseIsOverButton);
-    }
-
-    // If a telephone number is actually hovered, draw it.
-    if (m_hoveredTelephoneNumberData) {
-        drawHighlight(m_hoveredTelephoneNumberData-&gt;highlight.get(), graphicsContext);
-        return true;
-    }
-
-    return false;
-}
-
-bool ServicesOverlayController::mouseIsOverHighlight(DDHighlightRef highlight, bool&amp; mouseIsOverButton) const
-{
</del><span class="cx">     Boolean onButton;
</span><del>-    bool hovered = DDHighlightPointIsOnHighlight(highlight, (CGPoint)m_mousePosition, &amp;onButton);
</del><ins>+    bool hovered = DDHighlightPointIsOnHighlight(highlight.ddHighlight(), (CGPoint)m_mousePosition, &amp;onButton);
</ins><span class="cx">     mouseIsOverButton = onButton;
</span><span class="cx">     return hovered;
</span><span class="cx"> }
</span><span class="lines">@@ -336,16 +248,16 @@
</span><span class="cx"> {
</span><span class="cx">     // Highlight hysteresis is only for selection services, because telephone number highlights are already much more stable
</span><span class="cx">     // by virtue of being expanded to include the entire telephone number.
</span><del>-    if (m_hoveredTelephoneNumberData)
</del><ins>+    if (m_activeHighlight-&gt;type() == Highlight::Type::TelephoneNumber)
</ins><span class="cx">         return std::chrono::milliseconds::zero();
</span><span class="cx"> 
</span><span class="cx">     std::chrono::steady_clock::duration minimumTimeUntilHighlightShouldBeShown = 200_ms;
</span><span class="cx"> 
</span><span class="cx">     auto now = std::chrono::steady_clock::now();
</span><span class="cx">     auto timeSinceLastSelectionChange = now - m_lastSelectionChangeTime;
</span><del>-    auto timeSinceMouseOverSelection = now - m_lastHoveredHighlightChangeTime;
</del><ins>+    auto timeSinceHighlightBecameActive = now - m_lastActiveHighlightChangeTime;
</ins><span class="cx"> 
</span><del>-    return std::chrono::duration_cast&lt;std::chrono::milliseconds&gt;(std::max(minimumTimeUntilHighlightShouldBeShown - timeSinceLastSelectionChange, minimumTimeUntilHighlightShouldBeShown - timeSinceMouseOverSelection));
</del><ins>+    return std::chrono::duration_cast&lt;std::chrono::milliseconds&gt;(std::max(minimumTimeUntilHighlightShouldBeShown - timeSinceLastSelectionChange, minimumTimeUntilHighlightShouldBeShown - timeSinceHighlightBecameActive));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ServicesOverlayController::repaintHighlightTimerFired(WebCore::Timer&lt;ServicesOverlayController&gt;&amp;)
</span><span class="lines">@@ -354,10 +266,8 @@
</span><span class="cx">         m_servicesOverlay-&gt;setNeedsDisplay();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ServicesOverlayController::drawHighlight(DDHighlightRef highlight, WebCore::GraphicsContext&amp; graphicsContext)
</del><ins>+void ServicesOverlayController::drawHighlight(Highlight&amp; highlight, WebCore::GraphicsContext&amp; graphicsContext)
</ins><span class="cx"> {
</span><del>-    ASSERT(highlight);
-
</del><span class="cx">     bool mouseIsOverButton;
</span><span class="cx">     if (!mouseIsOverHighlight(highlight, mouseIsOverButton)) {
</span><span class="cx">         LOG(Services, &quot;ServicesOverlayController::drawHighlight - Mouse is not over highlight, so drawing nothing&quot;);
</span><span class="lines">@@ -372,86 +282,74 @@
</span><span class="cx"> 
</span><span class="cx">     CGContextRef cgContext = graphicsContext.platformContext();
</span><span class="cx">     
</span><del>-    CGLayerRef highlightLayer = DDHighlightGetLayerWithContext(highlight, cgContext);
-    CGRect highlightBoundingRect = DDHighlightGetBoundingRect(highlight);
-    
-    GraphicsContextStateSaver stateSaver(graphicsContext);
</del><ins>+    CGLayerRef highlightLayer = DDHighlightGetLayerWithContext(highlight.ddHighlight(), cgContext);
+    CGRect highlightBoundingRect = DDHighlightGetBoundingRect(highlight.ddHighlight());
</ins><span class="cx"> 
</span><del>-    graphicsContext.translate(toFloatSize(highlightBoundingRect.origin));
</del><ins>+    CGContextDrawLayerInRect(cgContext, highlightBoundingRect, highlightLayer);
+}
</ins><span class="cx"> 
</span><del>-    CGRect highlightDrawRect = highlightBoundingRect;
-    highlightDrawRect.origin.x = 0;
-    highlightDrawRect.origin.y = 0;
-    
-    CGContextDrawLayerInRect(cgContext, highlightDrawRect, highlightLayer);
</del><ins>+void ServicesOverlayController::drawRect(PageOverlay* overlay, WebCore::GraphicsContext&amp; graphicsContext, const WebCore::IntRect&amp; dirtyRect)
+{
+    bool mouseIsOverButton;
+    determineActiveHighlight(mouseIsOverButton);
+
+    if (m_activeHighlight)
+        drawHighlight(*m_activeHighlight, graphicsContext);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ServicesOverlayController::clearSelectionHighlight()
</del><ins>+void ServicesOverlayController::clearActiveHighlight()
</ins><span class="cx"> {
</span><del>-    if (!m_selectionHighlight)
</del><ins>+    if (!m_activeHighlight)
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    if (m_currentHoveredHighlight == m_selectionHighlight)
-        m_currentHoveredHighlight = nullptr;
-    if (m_currentMouseDownOnButtonHighlight == m_selectionHighlight)
</del><ins>+    if (m_currentMouseDownOnButtonHighlight == m_activeHighlight)
</ins><span class="cx">         m_currentMouseDownOnButtonHighlight = nullptr;
</span><del>-    m_selectionHighlight = nullptr;
</del><ins>+    m_activeHighlight = nullptr;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ServicesOverlayController::clearHoveredTelephoneNumberHighlight()
</del><ins>+void ServicesOverlayController::removeAllPotentialHighlightsOfType(Highlight::Type type)
</ins><span class="cx"> {
</span><del>-    if (!m_hoveredTelephoneNumberData)
-        return;
</del><ins>+    Vector&lt;RefPtr&lt;Highlight&gt;&gt; highlightsToRemove;
+    for (auto&amp; highlight : m_potentialHighlights) {
+        if (highlight-&gt;type() == type)
+            highlightsToRemove.append(highlight);
+    }
</ins><span class="cx"> 
</span><del>-    if (m_currentHoveredHighlight == m_hoveredTelephoneNumberData-&gt;highlight)
-        m_currentHoveredHighlight = nullptr;
-    if (m_currentMouseDownOnButtonHighlight == m_hoveredTelephoneNumberData-&gt;highlight)
-        m_currentMouseDownOnButtonHighlight = nullptr;
-    m_hoveredTelephoneNumberData = nullptr;
</del><ins>+    while (!highlightsToRemove.isEmpty())
+        m_potentialHighlights.remove(highlightsToRemove.takeLast());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ServicesOverlayController::establishHoveredTelephoneHighlight(bool&amp; mouseIsOverButton)
</del><ins>+void ServicesOverlayController::buildPhoneNumberHighlights()
</ins><span class="cx"> {
</span><del>-    ASSERT(m_currentTelephoneNumberRanges.size() == m_telephoneNumberHighlights.size());
</del><ins>+    removeAllPotentialHighlightsOfType(Highlight::Type::TelephoneNumber);
</ins><span class="cx"> 
</span><span class="cx">     for (unsigned i = 0; i &lt; m_currentTelephoneNumberRanges.size(); ++i) {
</span><del>-        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]);
</del><ins>+        // 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]);
</ins><span class="cx"> 
</span><del>-            // 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]-&gt;ownerDocument().view();
-            if (!viewForRange)
-                continue;
-            FrameView&amp; mainFrameView = *m_webPage-&gt;corePage()-&gt;mainFrame().view();
-            rect.setLocation(mainFrameView.windowToContents(viewForRange-&gt;contentsToWindow(rect.location())));
-
-            CGRect cgRect = rect;
-            m_telephoneNumberHighlights[i] = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, &amp;cgRect, 1, mainFrameView.visibleContentRect(), DDHighlightOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
-        }
-
-        if (!mouseIsOverHighlight(m_telephoneNumberHighlights[i].get(), mouseIsOverButton))
</del><ins>+        // 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]-&gt;ownerDocument().view();
+        if (!viewForRange)
</ins><span class="cx">             continue;
</span><ins>+        FrameView&amp; mainFrameView = *m_webPage.corePage()-&gt;mainFrame().view();
+        rect.setLocation(mainFrameView.windowToContents(viewForRange-&gt;contentsToWindow(rect.location())));
</ins><span class="cx"> 
</span><del>-        if (!m_hoveredTelephoneNumberData || m_hoveredTelephoneNumberData-&gt;highlight != m_telephoneNumberHighlights[i])
-            m_hoveredTelephoneNumberData = std::make_unique&lt;TelephoneNumberData&gt;(m_telephoneNumberHighlights[i], m_currentTelephoneNumberRanges[i]);
</del><ins>+        CGRect cgRect = rect;
+        RetainPtr&lt;DDHighlightRef&gt; ddHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, &amp;cgRect, 1, mainFrameView.visibleContentRect(), DDHighlightOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
</ins><span class="cx"> 
</span><del>-        m_servicesOverlay-&gt;setNeedsDisplay();
-        return;
</del><ins>+        m_potentialHighlights.add(Highlight::createForTelephoneNumber(ddHighlight, m_currentTelephoneNumberRanges[i]));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    clearHoveredTelephoneNumberHighlight();
-    mouseIsOverButton = false;
</del><ins>+    didRebuildPotentialHighlights();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ServicesOverlayController::maybeCreateSelectionHighlight()
</del><ins>+void ServicesOverlayController::buildSelectionHighlight()
</ins><span class="cx"> {
</span><del>-    ASSERT(!m_selectionHighlight);
-    ASSERT(m_servicesOverlay);
</del><ins>+    removeAllPotentialHighlightsOfType(Highlight::Type::Selection);
</ins><span class="cx"> 
</span><span class="cx">     Vector&lt;CGRect&gt; cgRects;
</span><span class="cx">     cgRects.reserveCapacity(m_currentSelectionRects.size());
</span><span class="lines">@@ -460,105 +358,163 @@
</span><span class="cx">         cgRects.append((CGRect)pixelSnappedIntRect(rect));
</span><span class="cx"> 
</span><span class="cx">     if (!cgRects.isEmpty()) {
</span><del>-        CGRect visibleRect = m_webPage-&gt;corePage()-&gt;mainFrame().view()-&gt;visibleContentRect();
-        m_selectionHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, cgRects.begin(), cgRects.size(), visibleRect, DDHighlightNoOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
</del><ins>+        CGRect visibleRect = m_webPage.corePage()-&gt;mainFrame().view()-&gt;visibleContentRect();
+        RetainPtr&lt;DDHighlightRef&gt; ddHighlight = adoptCF(DDHighlightCreateWithRectsInVisibleRectWithStyleAndDirection(nullptr, cgRects.begin(), cgRects.size(), visibleRect, DDHighlightNoOutlineWithArrow, YES, NSWritingDirectionNatural, NO, YES));
+        
+        m_potentialHighlights.add(Highlight::createForSelection(ddHighlight));
+    }
</ins><span class="cx"> 
</span><ins>+    didRebuildPotentialHighlights();
+}
+
+bool ServicesOverlayController::hasRelevantSelectionServices()
+{
+    return (m_isTextOnly &amp;&amp; WebProcess::shared().hasSelectionServices()) || WebProcess::shared().hasRichContentServices();
+}
+
+void ServicesOverlayController::didRebuildPotentialHighlights()
+{
+    if (m_potentialHighlights.isEmpty()) {
+        if (m_servicesOverlay)
+            m_webPage.uninstallPageOverlay(m_servicesOverlay);
+        return;
+    }
+
+    if (m_currentTelephoneNumberRanges.isEmpty() &amp;&amp; !hasRelevantSelectionServices())
+        return;
+
+    createOverlayIfNeeded();
+}
+
+void ServicesOverlayController::createOverlayIfNeeded()
+{
+    if (m_servicesOverlay) {
</ins><span class="cx">         m_servicesOverlay-&gt;setNeedsDisplay();
</span><ins>+        return;
</ins><span class="cx">     }
</span><ins>+
+    RefPtr&lt;PageOverlay&gt; overlay = PageOverlay::create(this, PageOverlay::OverlayType::Document);
+    m_servicesOverlay = overlay.get();
+    m_webPage.installPageOverlay(overlay.release(), PageOverlay::FadeMode::DoNotFade);
+    m_servicesOverlay-&gt;setNeedsDisplay();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool ServicesOverlayController::mouseEvent(PageOverlay*, const WebMouseEvent&amp; event)
</del><ins>+bool ServicesOverlayController::highlightsAreEquivalent(const Highlight* a, const Highlight* b)
</ins><span class="cx"> {
</span><del>-    m_mousePosition = m_webPage-&gt;corePage()-&gt;mainFrame().view()-&gt;rootViewToContents(event.position());
</del><ins>+    if (a == b)
+        return true;
</ins><span class="cx"> 
</span><del>-    DDHighlightRef oldHoveredHighlight = m_currentHoveredHighlight.get();
</del><ins>+    if (!a || !b)
+        return false;
</ins><span class="cx"> 
</span><del>-    bool mouseIsOverButton = false;
-    establishHoveredTelephoneHighlight(mouseIsOverButton);
-    if (m_hoveredTelephoneNumberData) {
-        ASSERT(m_hoveredTelephoneNumberData-&gt;highlight);
-        m_currentHoveredHighlight = m_hoveredTelephoneNumberData-&gt;highlight;
-    } else {
-        if (!m_selectionHighlight)
-            maybeCreateSelectionHighlight();
</del><ins>+    if (a-&gt;type() == Highlight::Type::TelephoneNumber &amp;&amp; b-&gt;type() == Highlight::Type::TelephoneNumber &amp;&amp; areRangesEqual(a-&gt;range(), b-&gt;range()))
+        return true;
</ins><span class="cx"> 
</span><del>-        if (m_selectionHighlight &amp;&amp; mouseIsOverHighlight(m_selectionHighlight.get(), mouseIsOverButton))
-            m_currentHoveredHighlight = m_selectionHighlight;
-        else
-            m_currentHoveredHighlight = nullptr;
</del><ins>+    return false;
+}
+
+void ServicesOverlayController::determineActiveHighlight(bool&amp; mouseIsOverActiveHighlightButton)
+{
+    mouseIsOverActiveHighlightButton = false;
+
+    RefPtr&lt;Highlight&gt; oldActiveHighlight = m_activeHighlight.release();
+
+    for (auto&amp; highlight : m_potentialHighlights) {
+        if (highlight-&gt;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 &amp;&amp; m_activeHighlight-&gt;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 (m_currentTelephoneNumberRanges.isEmpty() &amp;&amp; !hasRelevantSelectionServices())
+                continue;
+        }
+
+        // If this highlight isn't hovered, it can't be active.
+        bool mouseIsOverButton;
+        if (!mouseIsOverHighlight(*highlight, mouseIsOverButton))
+            continue;
+
+        m_activeHighlight = highlight;
+        mouseIsOverActiveHighlightButton = mouseIsOverButton;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (oldHoveredHighlight != m_currentHoveredHighlight) {
-        m_lastHoveredHighlightChangeTime = std::chrono::steady_clock::now();
</del><ins>+    if (!highlightsAreEquivalent(oldActiveHighlight.get(), m_activeHighlight.get())) {
+        m_lastActiveHighlightChangeTime = std::chrono::steady_clock::now();
</ins><span class="cx">         m_servicesOverlay-&gt;setNeedsDisplay();
</span><ins>+        m_currentMouseDownOnButtonHighlight = nullptr;
</ins><span class="cx">     }
</span><ins>+}
</ins><span class="cx"> 
</span><del>-    // If this event has nothing to do with the left button, it clears the current mouse down tracking and we're done processing it.
</del><ins>+bool ServicesOverlayController::mouseEvent(PageOverlay*, const WebMouseEvent&amp; event)
+{
+    m_mousePosition = m_webPage.corePage()-&gt;mainFrame().view()-&gt;rootViewToContents(event.position());
+
+    bool mouseIsOverActiveHighlightButton = false;
+    determineActiveHighlight(mouseIsOverActiveHighlightButton);
+
+    // Cancel the potential click if any button other than the left button changes state, and ignore the event.
</ins><span class="cx">     if (event.button() != WebMouseEvent::LeftButton) {
</span><span class="cx">         m_currentMouseDownOnButtonHighlight = nullptr;
</span><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // Check and see if the mouse went up and we have a current mouse down highlight button.
</del><ins>+    // If the mouse lifted while still over the highlight button that it went down on, then that is a click.
</ins><span class="cx">     if (event.type() == WebEvent::MouseUp) {
</span><del>-        RetainPtr&lt;DDHighlightRef&gt; mouseDownHighlight = WTF::move(m_currentMouseDownOnButtonHighlight);
</del><ins>+        RefPtr&lt;Highlight&gt; mouseDownHighlight = m_currentMouseDownOnButtonHighlight;
+        m_currentMouseDownOnButtonHighlight = nullptr;
</ins><span class="cx"> 
</span><del>-        // If the mouse lifted while still over the highlight button that it went down on, then that is a click.
-        if (mouseIsOverButton &amp;&amp; mouseDownHighlight &amp;&amp; remainingTimeUntilHighlightShouldBeShown() &lt;= std::chrono::steady_clock::duration::zero()) {
-            handleClick(m_mousePosition, mouseDownHighlight.get());
</del><ins>+        if (mouseIsOverActiveHighlightButton &amp;&amp; mouseDownHighlight &amp;&amp; remainingTimeUntilHighlightShouldBeShown() &lt;= std::chrono::steady_clock::duration::zero()) {
+            handleClick(m_mousePosition, *mouseDownHighlight);
</ins><span class="cx">             return true;
</span><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // Check and see if the mouse moved within the confines of the DD highlight button.
</del><ins>+    // If the mouse moved outside of the button tracking a potential click, stop tracking the click.
</ins><span class="cx">     if (event.type() == WebEvent::MouseMove) {
</span><del>-        // Moving with the mouse button down is okay as long as the mouse never leaves the highlight button.
-        if (m_currentMouseDownOnButtonHighlight &amp;&amp; mouseIsOverButton)
</del><ins>+        if (m_currentMouseDownOnButtonHighlight &amp;&amp; mouseIsOverActiveHighlightButton)
</ins><span class="cx">             return true;
</span><span class="cx"> 
</span><span class="cx">         m_currentMouseDownOnButtonHighlight = nullptr;
</span><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // Check and see if the mouse went down over a DD highlight button.
</del><ins>+    // If the mouse went down over the active highlight's button, track this as a potential click.
</ins><span class="cx">     if (event.type() == WebEvent::MouseDown) {
</span><del>-        if (m_currentHoveredHighlight &amp;&amp; mouseIsOverButton) {
-            m_currentMouseDownOnButtonHighlight = m_currentHoveredHighlight;
</del><ins>+        if (m_activeHighlight &amp;&amp; mouseIsOverActiveHighlightButton) {
+            m_currentMouseDownOnButtonHighlight = m_activeHighlight;
</ins><span class="cx">             m_servicesOverlay-&gt;setNeedsDisplay();
</span><span class="cx">             return true;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         return false;
</span><span class="cx">     }
</span><del>-        
</del><ins>+
</ins><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ServicesOverlayController::handleClick(const WebCore::IntPoint&amp; clickPoint, DDHighlightRef highlight)
</del><ins>+void ServicesOverlayController::handleClick(const WebCore::IntPoint&amp; clickPoint, Highlight&amp; highlight)
</ins><span class="cx"> {
</span><del>-    ASSERT(highlight);
-
-    FrameView* frameView = m_webPage-&gt;mainFrameView();
</del><ins>+    FrameView* frameView = m_webPage.mainFrameView();
</ins><span class="cx">     if (!frameView)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     IntPoint windowPoint = frameView-&gt;contentsToWindow(clickPoint);
</span><span class="cx"> 
</span><del>-    if (highlight == m_selectionHighlight) {
</del><ins>+    if (highlight.type() == Highlight::Type::Selection) {
</ins><span class="cx">         Vector&lt;String&gt; selectedTelephoneNumbers;
</span><span class="cx">         selectedTelephoneNumbers.reserveCapacity(m_currentTelephoneNumberRanges.size());
</span><span class="cx">         for (auto&amp; range : m_currentTelephoneNumberRanges)
</span><span class="cx">             selectedTelephoneNumbers.append(range-&gt;text());
</span><span class="cx"> 
</span><del>-        m_webPage-&gt;handleSelectionServiceClick(m_webPage-&gt;corePage()-&gt;mainFrame().selection(), selectedTelephoneNumbers, windowPoint);
-    } else if (m_hoveredTelephoneNumberData &amp;&amp; m_hoveredTelephoneNumberData-&gt;highlight == highlight)
-        m_webPage-&gt;handleTelephoneNumberClick(m_hoveredTelephoneNumberData-&gt;range-&gt;text(), windowPoint);
-    else
-        ASSERT_NOT_REACHED();
</del><ins>+        m_webPage.handleSelectionServiceClick(m_webPage.corePage()-&gt;mainFrame().selection(), selectedTelephoneNumbers, windowPoint);
+    } else if (highlight.type() == Highlight::Type::TelephoneNumber)
+        m_webPage.handleTelephoneNumberClick(highlight.range()-&gt;text(), windowPoint);
</ins><span class="cx"> }
</span><del>-    
</del><ins>+
</ins><span class="cx"> } // namespace WebKit
</span><span class="cx"> 
</span><span class="cx"> #endif // #if ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION) &amp;&amp; PLATFORM(MAC)
</span></span></pre>
</div>
</div>

</body>
</html>