<!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>[184204] trunk/Source/WebCore</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/184204">184204</a></dd>
<dt>Author</dt> <dd>eric.carlson@apple.com</dd>
<dt>Date</dt> <dd>2015-05-12 09:41:48 -0700 (Tue, 12 May 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Mac] Refine media playback target client configuration
https://bugs.webkit.org/show_bug.cgi?id=144892

Reviewed by Brent Fulgham.

Client and target picker state changes fequently happen several times in quick succession, so
don't react to immediately so we can batch callbacks to the web process. 
* Modules/mediasession/WebMediaSessionManager.cpp:
(WebCore::ClientState::ClientState): Store the client as a reference rather than a pointer
because it can never be NULL.
(WebCore::ClientState::operator == ): New.
(WebCore::WebMediaSessionManager::addPlaybackTargetPickerClient): Schedule the initial client 
configuration and a target configuration check.
(WebCore::WebMediaSessionManager::removePlaybackTargetPickerClient): Schedule a target monitoring
update, and a target configuration check.
(WebCore::WebMediaSessionManager::removeAllPlaybackTargetPickerClients): Ditto.
(WebCore::WebMediaSessionManager::showPlaybackTargetPicker): Schedule a target monitoring update.
(WebCore::WebMediaSessionManager::clientStateDidChange): If the client whose state has changed
can play to a target, tell it to start using the target even if it isn't playing as long as
no other client is actively using a target.
(WebCore::WebMediaSessionManager::setPlaybackTarget): Configure clients after a pause.
(WebCore::WebMediaSessionManager::configureNewClients): New, do new client configuration.
(WebCore::WebMediaSessionManager::configurePlaybackTargetClients): New, configure target clients.
(WebCore::WebMediaSessionManager::scheduleDelayedTask): Schedule the timer.
(WebCore::WebMediaSessionManager::taskTimerFired): Execute delayed tasks.
(WebCore::WebMediaSessionManager::find): 
* Modules/mediasession/WebMediaSessionManager.h:

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::setMuted): Call updateMediaState.
(WebCore::HTMLMediaElement::setPlaying): Ditto.
(WebCore::HTMLMediaElement::mediaPlayerCurrentPlaybackTargetIsWirelessChanged): Ditto.
(WebCore::HTMLMediaElement::enqueuePlaybackTargetAvailabilityChangedEvent): Expand logging.
(WebCore::HTMLMediaElement::updateMediaState): New, don't broadcast a media state change
unless something actually changed.
* html/HTMLMediaElement.h:

* html/HTMLMediaSession.cpp:
(WebCore::HTMLMediaSession::externalOutputDeviceAvailableDidChange): Update logging.
(WebCore::HTMLMediaSession::setShouldPlayToPlaybackTarget): Ditto.
(WebCore::HTMLMediaSession::mediaEngineUpdated): Cleanup.

* platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm:
(WebCore::MediaPlaybackTargetPickerMac::showPlaybackTargetPicker): Remove the call to
deprecated API and the &quot;-Wdeprecated-declarations&quot;.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesmediasessionWebMediaSessionManagercpp">trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesmediasessionWebMediaSessionManagerh">trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaElementcpp">trunk/Source/WebCore/html/HTMLMediaElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaElementh">trunk/Source/WebCore/html/HTMLMediaElement.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaSessioncpp">trunk/Source/WebCore/html/HTMLMediaSession.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaPlaybackTargetPickerMacmm">trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (184203 => 184204)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-05-12 16:25:32 UTC (rev 184203)
+++ trunk/Source/WebCore/ChangeLog        2015-05-12 16:41:48 UTC (rev 184204)
</span><span class="lines">@@ -1,3 +1,51 @@
</span><ins>+2015-05-12  Eric Carlson  &lt;eric.carlson@apple.com&gt;
+
+        [Mac] Refine media playback target client configuration
+        https://bugs.webkit.org/show_bug.cgi?id=144892
+
+        Reviewed by Brent Fulgham.
+
+        Client and target picker state changes fequently happen several times in quick succession, so
+        don't react to immediately so we can batch callbacks to the web process. 
+        * Modules/mediasession/WebMediaSessionManager.cpp:
+        (WebCore::ClientState::ClientState): Store the client as a reference rather than a pointer
+        because it can never be NULL.
+        (WebCore::ClientState::operator == ): New.
+        (WebCore::WebMediaSessionManager::addPlaybackTargetPickerClient): Schedule the initial client 
+        configuration and a target configuration check.
+        (WebCore::WebMediaSessionManager::removePlaybackTargetPickerClient): Schedule a target monitoring
+        update, and a target configuration check.
+        (WebCore::WebMediaSessionManager::removeAllPlaybackTargetPickerClients): Ditto.
+        (WebCore::WebMediaSessionManager::showPlaybackTargetPicker): Schedule a target monitoring update.
+        (WebCore::WebMediaSessionManager::clientStateDidChange): If the client whose state has changed
+        can play to a target, tell it to start using the target even if it isn't playing as long as
+        no other client is actively using a target.
+        (WebCore::WebMediaSessionManager::setPlaybackTarget): Configure clients after a pause.
+        (WebCore::WebMediaSessionManager::configureNewClients): New, do new client configuration.
+        (WebCore::WebMediaSessionManager::configurePlaybackTargetClients): New, configure target clients.
+        (WebCore::WebMediaSessionManager::scheduleDelayedTask): Schedule the timer.
+        (WebCore::WebMediaSessionManager::taskTimerFired): Execute delayed tasks.
+        (WebCore::WebMediaSessionManager::find): 
+        * Modules/mediasession/WebMediaSessionManager.h:
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::setMuted): Call updateMediaState.
+        (WebCore::HTMLMediaElement::setPlaying): Ditto.
+        (WebCore::HTMLMediaElement::mediaPlayerCurrentPlaybackTargetIsWirelessChanged): Ditto.
+        (WebCore::HTMLMediaElement::enqueuePlaybackTargetAvailabilityChangedEvent): Expand logging.
+        (WebCore::HTMLMediaElement::updateMediaState): New, don't broadcast a media state change
+        unless something actually changed.
+        * html/HTMLMediaElement.h:
+
+        * html/HTMLMediaSession.cpp:
+        (WebCore::HTMLMediaSession::externalOutputDeviceAvailableDidChange): Update logging.
+        (WebCore::HTMLMediaSession::setShouldPlayToPlaybackTarget): Ditto.
+        (WebCore::HTMLMediaSession::mediaEngineUpdated): Cleanup.
+
+        * platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm:
+        (WebCore::MediaPlaybackTargetPickerMac::showPlaybackTargetPicker): Remove the call to
+        deprecated API and the &quot;-Wdeprecated-declarations&quot;.
+
</ins><span class="cx"> 2015-05-12  Joanmarie Diggs  &lt;jdiggs@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         AX: [GTK] Defer to WebCore Accessibility for table exposure
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasessionWebMediaSessionManagercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp (184203 => 184204)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp        2015-05-12 16:25:32 UTC (rev 184203)
+++ trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.cpp        2015-05-12 16:41:48 UTC (rev 184204)
</span><span class="lines">@@ -34,17 +34,25 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+static double taskDelayInterval = 1.0 / 10.0;
+
</ins><span class="cx"> struct ClientState {
</span><span class="cx">     explicit ClientState(WebMediaSessionManagerClient&amp; client, uint64_t contextId)
</span><del>-        : client(&amp;client)
</del><ins>+        : client(client)
</ins><span class="cx">         , contextId(contextId)
</span><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    WebMediaSessionManagerClient* client { nullptr };
</del><ins>+    bool operator == (ClientState const&amp; other) const
+    {
+        return contextId == other.contextId &amp;&amp; &amp;client == &amp;other.client;
+    }
+
+    WebMediaSessionManagerClient&amp; client;
</ins><span class="cx">     uint64_t contextId { 0 };
</span><span class="cx">     WebCore::MediaProducer::MediaStateFlags flags { WebCore::MediaProducer::IsNotPlaying };
</span><span class="cx">     bool requestedPicker { false };
</span><ins>+    bool configurationRequired { true };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> static bool flagsAreSet(MediaProducer::MediaStateFlags value, unsigned flags)
</span><span class="lines">@@ -70,25 +78,9 @@
</span><span class="cx"> 
</span><span class="cx">     m_clientState.append(std::make_unique&lt;ClientState&gt;(client, contextId));
</span><span class="cx"> 
</span><del>-    if (m_externalOutputDeviceAvailable || m_playbackTarget) {
</del><ins>+    if (m_externalOutputDeviceAvailable || m_playbackTarget)
+        scheduleDelayedTask(InitialConfigurationTask | TargetClientsConfigurationTask);
</ins><span class="cx"> 
</span><del>-        TaskCallback callback = std::make_tuple(&amp;client, contextId, [this](ClientState&amp; state) {
-
-            if (m_externalOutputDeviceAvailable)
-                state.client-&gt;externalOutputDeviceAvailableDidChange(state.contextId, true);
-
-            if (m_playbackTarget) {
-                state.client-&gt;setPlaybackTarget(state.contextId, *m_playbackTarget.copyRef());
-
-                if (m_clientState.size() == 1 &amp;&amp; m_playbackTarget-&gt;hasActiveRoute())
-                    state.client-&gt;setShouldPlayToPlaybackTarget(state.contextId, true);
-            }
-        });
-
-        m_taskQueue.append(callback);
-        m_taskTimer.startOneShot(0);
-    }
-
</del><span class="cx">     return contextId;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -100,18 +92,16 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     m_clientState.remove(index);
</span><del>-    configurePlaybackTargetMonitoring();
-
-    if (m_playbackTarget &amp;&amp; m_clientState.size() == 1 &amp;&amp; m_playbackTarget-&gt;hasActiveRoute())
-        m_clientState[0]-&gt;client-&gt;setShouldPlayToPlaybackTarget(m_clientState[0]-&gt;contextId, true);
</del><ins>+    scheduleDelayedTask(TargetMonitoringConfigurationTask | TargetClientsConfigurationTask);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebMediaSessionManager::removeAllPlaybackTargetPickerClients(WebMediaSessionManagerClient&amp; client)
</span><span class="cx"> {
</span><span class="cx">     for (size_t i = m_clientState.size(); i &gt; 0; --i) {
</span><del>-        if (m_clientState[i - 1]-&gt;client == &amp;client)
</del><ins>+        if (&amp;m_clientState[i - 1]-&gt;client == &amp;client)
</ins><span class="cx">             m_clientState.remove(i - 1);
</span><span class="cx">     }
</span><ins>+    scheduleDelayedTask(TargetMonitoringConfigurationTask | TargetClientsConfigurationTask);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebMediaSessionManager::showPlaybackTargetPicker(WebMediaSessionManagerClient&amp; client, uint64_t contextId, const IntRect&amp; rect, bool)
</span><span class="lines">@@ -121,8 +111,9 @@
</span><span class="cx">     if (index == notFound)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><ins>+    auto&amp; clientRequestingPicker = m_clientState[index];
</ins><span class="cx">     for (auto&amp; state : m_clientState)
</span><del>-        state-&gt;requestedPicker = (state-&gt;contextId == contextId &amp;&amp; state-&gt;client == &amp;client);
</del><ins>+        state-&gt;requestedPicker = state == clientRequestingPicker;
</ins><span class="cx"> 
</span><span class="cx">     bool hasActiveRoute = flagsAreSet(m_clientState[index]-&gt;flags, MediaProducer::IsPlayingToExternalDevice);
</span><span class="cx">     targetPicker().showPlaybackTargetPicker(FloatRect(rect), hasActiveRoute);
</span><span class="lines">@@ -142,30 +133,35 @@
</span><span class="cx"> 
</span><span class="cx">     changedClientState-&gt;flags = newFlags;
</span><span class="cx">     if (!flagsAreSet(oldFlags, MediaProducer::RequiresPlaybackTargetMonitoring) &amp;&amp; flagsAreSet(newFlags, MediaProducer::RequiresPlaybackTargetMonitoring))
</span><del>-        configurePlaybackTargetMonitoring();
</del><ins>+        scheduleDelayedTask(TargetMonitoringConfigurationTask);
</ins><span class="cx"> 
</span><del>-    if (!flagsAreSet(newFlags, MediaProducer::IsPlayingVideo) || !flagsAreSet(newFlags, MediaProducer::ExternalDeviceAutoPlayCandidate))
</del><ins>+    if (!flagsAreSet(newFlags, MediaProducer::ExternalDeviceAutoPlayCandidate))
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (!m_playbackTarget || !m_playbackTarget-&gt;hasActiveRoute())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     // Do not interrupt another element already playing to a device.
</span><ins>+    bool anotherClientHasActiveTarget = false;
</ins><span class="cx">     for (auto&amp; state : m_clientState) {
</span><del>-        if (state-&gt;contextId == contextId &amp;&amp; state-&gt;client == &amp;client)
-            continue;
-
-        if (flagsAreSet(state-&gt;flags, MediaProducer::IsPlayingVideo) &amp;&amp; flagsAreSet(state-&gt;flags, MediaProducer::IsPlayingToExternalDevice))
-            return;
</del><ins>+        if (flagsAreSet(state-&gt;flags, MediaProducer::IsPlayingToExternalDevice)) {
+            if (flagsAreSet(state-&gt;flags, MediaProducer::IsPlayingVideo))
+                return;
+            anotherClientHasActiveTarget = true;
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    // Do not take the target if another client has it and the client reporting a state change is not playing.
+    if (anotherClientHasActiveTarget &amp;&amp; !flagsAreSet(newFlags, MediaProducer::IsPlayingVideo))
+        return;
+
</ins><span class="cx">     for (auto&amp; state : m_clientState) {
</span><del>-        if (state-&gt;contextId == contextId &amp;&amp; state-&gt;client == &amp;client)
</del><ins>+        if (state == changedClientState)
</ins><span class="cx">             continue;
</span><del>-        state-&gt;client-&gt;setShouldPlayToPlaybackTarget(state-&gt;contextId, false);
</del><ins>+        state-&gt;client.setShouldPlayToPlaybackTarget(state-&gt;contextId, false);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    changedClientState-&gt;client-&gt;setShouldPlayToPlaybackTarget(changedClientState-&gt;contextId, true);
</del><ins>+    changedClientState-&gt;client.setShouldPlayToPlaybackTarget(changedClientState-&gt;contextId, true);
</ins><span class="cx"> 
</span><span class="cx">     if (index &amp;&amp; m_clientState.size() &gt; 1)
</span><span class="cx">         std::swap(m_clientState.at(index), m_clientState.at(0));
</span><span class="lines">@@ -174,32 +170,70 @@
</span><span class="cx"> void WebMediaSessionManager::setPlaybackTarget(Ref&lt;MediaPlaybackTarget&gt;&amp;&amp; target)
</span><span class="cx"> {
</span><span class="cx">     m_playbackTarget = WTF::move(target);
</span><ins>+    scheduleDelayedTask(TargetClientsConfigurationTask);
+}
</ins><span class="cx"> 
</span><del>-    size_t indexThatRequestedPicker = notFound;
</del><ins>+void WebMediaSessionManager::externalOutputDeviceAvailableDidChange(bool available)
+{
+    m_externalOutputDeviceAvailable = available;
+    for (auto&amp; state : m_clientState)
+        state-&gt;client.externalOutputDeviceAvailableDidChange(state-&gt;contextId, available);
+}
+
+void WebMediaSessionManager::configureNewClients()
+{
+    for (auto&amp; state : m_clientState) {
+        if (!state-&gt;configurationRequired)
+            continue;
+
+        state-&gt;configurationRequired = false;
+        if (m_externalOutputDeviceAvailable)
+            state-&gt;client.externalOutputDeviceAvailableDidChange(state-&gt;contextId, true);
+
+        if (m_playbackTarget)
+            state-&gt;client.setPlaybackTarget(state-&gt;contextId, *m_playbackTarget.copyRef());
+    }
+}
+
+void WebMediaSessionManager::configurePlaybackTargetClients()
+{
+    size_t indexOfClientThatRequestedPicker = notFound;
+    size_t indexOfAutoPlayCandidate = notFound;
+    size_t indexOfClientWillPlayToTarget = notFound;
+    bool haveActiveRoute = m_playbackTarget &amp;&amp; m_playbackTarget-&gt;hasActiveRoute();
+
</ins><span class="cx">     for (size_t i = 0; i &lt; m_clientState.size(); ++i) {
</span><span class="cx">         auto&amp; state = m_clientState[i];
</span><del>-        state-&gt;client-&gt;setPlaybackTarget(state-&gt;contextId, *m_playbackTarget.copyRef());
-        if (state-&gt;requestedPicker) {
-            indexThatRequestedPicker = i;
-            continue;
-        }
-        state-&gt;client-&gt;setShouldPlayToPlaybackTarget(state-&gt;contextId, false);
-        state-&gt;requestedPicker = false;
</del><ins>+
+        if (indexOfClientThatRequestedPicker == notFound &amp;&amp; state-&gt;requestedPicker)
+            indexOfClientThatRequestedPicker = i;
+
+        if (indexOfClientWillPlayToTarget == notFound &amp;&amp; flagsAreSet(state-&gt;flags, MediaProducer::IsPlayingToExternalDevice))
+            indexOfClientWillPlayToTarget = i;
+
+        if (indexOfAutoPlayCandidate == notFound &amp;&amp; flagsAreSet(state-&gt;flags, MediaProducer::ExternalDeviceAutoPlayCandidate) &amp;&amp; !flagsAreSet(state-&gt;flags, MediaProducer::IsPlayingVideo))
+            indexOfAutoPlayCandidate = i;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (indexThatRequestedPicker == notFound)
-        return;
</del><ins>+    if (indexOfClientThatRequestedPicker != notFound)
+        indexOfClientWillPlayToTarget = indexOfClientThatRequestedPicker;
+    if (indexOfClientWillPlayToTarget == notFound &amp;&amp; haveActiveRoute &amp;&amp; indexOfAutoPlayCandidate != notFound)
+        indexOfClientWillPlayToTarget = indexOfAutoPlayCandidate;
</ins><span class="cx"> 
</span><del>-    auto&amp; state = m_clientState[indexThatRequestedPicker];
-    state-&gt;client-&gt;setShouldPlayToPlaybackTarget(state-&gt;contextId, m_playbackTarget &amp;&amp; m_playbackTarget-&gt;hasActiveRoute());
-    state-&gt;requestedPicker = false;
-}
</del><ins>+    for (size_t i = 0; i &lt; m_clientState.size(); ++i) {
+        auto&amp; state = m_clientState[i];
</ins><span class="cx"> 
</span><del>-void WebMediaSessionManager::externalOutputDeviceAvailableDidChange(bool available)
-{
-    m_externalOutputDeviceAvailable = available;
-    for (auto&amp; state : m_clientState)
-        state-&gt;client-&gt;externalOutputDeviceAvailableDidChange(state-&gt;contextId, available);
</del><ins>+        if (m_playbackTarget)
+            state-&gt;client.setPlaybackTarget(state-&gt;contextId, *m_playbackTarget.copyRef());
+
+        if (i != indexOfClientWillPlayToTarget)
+            state-&gt;client.setShouldPlayToPlaybackTarget(state-&gt;contextId, false);
+        else if (!flagsAreSet(state-&gt;flags, MediaProducer::IsPlayingToExternalDevice))
+            state-&gt;client.setShouldPlayToPlaybackTarget(state-&gt;contextId, haveActiveRoute);
+
+        state-&gt;configurationRequired = false;
+        state-&gt;requestedPicker = false;
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebMediaSessionManager::configurePlaybackTargetMonitoring()
</span><span class="lines">@@ -218,26 +252,28 @@
</span><span class="cx">         targetPicker().stopMonitoringPlaybackTargets();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebMediaSessionManager::scheduleDelayedTask(ConfigurationTasks tasks)
+{
+    m_taskFlags |= tasks;
+    m_taskTimer.startOneShot(taskDelayInterval);
+}
+
</ins><span class="cx"> void WebMediaSessionManager::taskTimerFired()
</span><span class="cx"> {
</span><del>-    auto taskQueue = WTF::move(m_taskQueue);
-    if (taskQueue.isEmpty())
-        return;
</del><ins>+    if (m_taskFlags &amp; InitialConfigurationTask)
+        configureNewClients();
+    if (m_taskFlags &amp; TargetClientsConfigurationTask)
+        configurePlaybackTargetClients();
+    if (m_taskFlags &amp; TargetMonitoringConfigurationTask)
+        configurePlaybackTargetMonitoring();
</ins><span class="cx"> 
</span><del>-    for (auto&amp; task : taskQueue) {
-        size_t index = find(std::get&lt;0&gt;(task), std::get&lt;1&gt;(task));
-
-        if (index == notFound)
-            continue;
-
-        std::get&lt;2&gt;(task)(*m_clientState[index]);
-    }
</del><ins>+    m_taskFlags = NoTask;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> size_t WebMediaSessionManager::find(WebMediaSessionManagerClient* client, uint64_t contextId)
</span><span class="cx"> {
</span><span class="cx">     for (size_t i = 0; i &lt; m_clientState.size(); ++i) {
</span><del>-        if (m_clientState[i]-&gt;contextId == contextId &amp;&amp; m_clientState[i]-&gt;client == client)
</del><ins>+        if (m_clientState[i]-&gt;contextId == contextId &amp;&amp; &amp;m_clientState[i]-&gt;client == client)
</ins><span class="cx">             return i;
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmediasessionWebMediaSessionManagerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h (184203 => 184204)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h        2015-05-12 16:25:32 UTC (rev 184203)
+++ trunk/Source/WebCore/Modules/mediasession/WebMediaSessionManager.h        2015-05-12 16:41:48 UTC (rev 184204)
</span><span class="lines">@@ -64,15 +64,25 @@
</span><span class="cx">     virtual void externalOutputDeviceAvailableDidChange(bool) override;
</span><span class="cx"> 
</span><span class="cx">     size_t find(WebMediaSessionManagerClient*, uint64_t);
</span><ins>+    void configurePlaybackTargetClients();
+    void configureNewClients();
</ins><span class="cx">     void configurePlaybackTargetMonitoring();
</span><ins>+
+    enum ConfigurationTaskFlags {
+        NoTask = 0,
+        InitialConfigurationTask = 1 &lt;&lt; 0,
+        TargetClientsConfigurationTask = 1 &lt;&lt; 1,
+        TargetMonitoringConfigurationTask = 1 &lt;&lt; 2,
+    };
+    typedef unsigned ConfigurationTasks;
+
+    void scheduleDelayedTask(ConfigurationTasks);
</ins><span class="cx">     void taskTimerFired();
</span><del>-
-    typedef std::tuple&lt;WebMediaSessionManagerClient*, uint64_t, std::function&lt;void(ClientState&amp;)&gt;&gt; TaskCallback;
-    Vector&lt;TaskCallback&gt; m_taskQueue;
</del><span class="cx">     RunLoop::Timer&lt;WebMediaSessionManager&gt; m_taskTimer;
</span><span class="cx"> 
</span><span class="cx">     Vector&lt;std::unique_ptr&lt;ClientState&gt;&gt; m_clientState;
</span><span class="cx">     RefPtr&lt;MediaPlaybackTarget&gt; m_playbackTarget;
</span><ins>+    ConfigurationTasks m_taskFlags { NoTask };
</ins><span class="cx">     bool m_externalOutputDeviceAvailable { false };
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (184203 => 184204)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.cpp        2015-05-12 16:25:32 UTC (rev 184203)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp        2015-05-12 16:41:48 UTC (rev 184204)
</span><span class="lines">@@ -3034,7 +3034,7 @@
</span><span class="cx">         document().updateIsPlayingMedia();
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(WIRELESS_PLAYBACK_TARGET)
</span><del>-        m_mediaSession-&gt;mediaStateDidChange(*this, mediaState());
</del><ins>+        updateMediaState();
</ins><span class="cx"> #endif
</span><span class="cx">     }
</span><span class="cx"> #endif
</span><span class="lines">@@ -4632,7 +4632,7 @@
</span><span class="cx">     document().updateIsPlayingMedia();
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(WIRELESS_PLAYBACK_TARGET)
</span><del>-    m_mediaSession-&gt;mediaStateDidChange(*this, mediaState());
</del><ins>+    updateMediaState();
</ins><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -4895,7 +4895,7 @@
</span><span class="cx"> 
</span><span class="cx">     configureMediaControls();
</span><span class="cx">     scheduleEvent(eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent);
</span><del>-    m_mediaSession-&gt;mediaStateDidChange(*this, mediaState());
</del><ins>+    updateMediaState();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool HTMLMediaElement::dispatchEvent(PassRefPtr&lt;Event&gt; prpEvent)
</span><span class="lines">@@ -4946,8 +4946,9 @@
</span><span class="cx"> 
</span><span class="cx"> void HTMLMediaElement::enqueuePlaybackTargetAvailabilityChangedEvent()
</span><span class="cx"> {
</span><del>-    LOG(Media, &quot;HTMLMediaElement::enqueuePlaybackTargetAvailabilityChangedEvent(%p)&quot;, this);
-    RefPtr&lt;Event&gt; event = WebKitPlaybackTargetAvailabilityEvent::create(eventNames().webkitplaybacktargetavailabilitychangedEvent, m_mediaSession-&gt;hasWirelessPlaybackTargets(*this));
</del><ins>+    bool hasTargets = m_mediaSession-&gt;hasWirelessPlaybackTargets(*this);
+    LOG(Media, &quot;HTMLMediaElement::enqueuePlaybackTargetAvailabilityChangedEvent(%p) - hasTargets = %s&quot;, this, boolString(hasTargets));
+    RefPtr&lt;Event&gt; event = WebKitPlaybackTargetAvailabilityEvent::create(eventNames().webkitplaybacktargetavailabilitychangedEvent, hasTargets);
</ins><span class="cx">     event-&gt;setTarget(this);
</span><span class="cx">     m_asyncEventQueue.enqueueEvent(event.release());
</span><span class="cx"> }
</span><span class="lines">@@ -6218,6 +6219,18 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+void HTMLMediaElement::updateMediaState()
+{
+    MediaProducer::MediaStateFlags state = mediaState();
+    if (m_mediaState == state)
+        return;
+
+    m_mediaState = state;
+    m_mediaSession-&gt;mediaStateDidChange(*this, m_mediaState);
+}
+#endif
+
</ins><span class="cx"> MediaProducer::MediaStateFlags HTMLMediaElement::mediaState() const
</span><span class="cx"> {
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (184203 => 184204)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.h        2015-05-12 16:25:32 UTC (rev 184203)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h        2015-05-12 16:41:48 UTC (rev 184204)
</span><span class="lines">@@ -740,6 +740,7 @@
</span><span class="cx"> #if ENABLE(WIRELESS_PLAYBACK_TARGET)
</span><span class="cx">     virtual void documentWillSuspendForPageCache() override final;
</span><span class="cx">     virtual void documentDidResumeFromPageCache() override final;
</span><ins>+    void updateMediaState();
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     Timer m_pendingActionTimer;
</span><span class="lines">@@ -931,6 +932,7 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(WIRELESS_PLAYBACK_TARGET)
</span><ins>+    MediaProducer::MediaStateFlags m_mediaState { MediaProducer::IsNotPlaying };
</ins><span class="cx">     bool m_hasPlaybackTargetAvailabilityListeners { false };
</span><span class="cx"> #endif
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaSessioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaSession.cpp (184203 => 184204)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaSession.cpp        2015-05-12 16:25:32 UTC (rev 184203)
+++ trunk/Source/WebCore/html/HTMLMediaSession.cpp        2015-05-12 16:41:48 UTC (rev 184204)
</span><span class="lines">@@ -305,11 +305,10 @@
</span><span class="cx">     if (m_hasPlaybackTargets == hasTargets)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    LOG(Media, &quot;HTMLMediaSession::externalOutputDeviceAvailableDidChange - hasTargets %s&quot;, hasTargets ? &quot;TRUE&quot; : &quot;FALSE&quot;);
</del><ins>+    LOG(Media, &quot;HTMLMediaSession::externalOutputDeviceAvailableDidChange(%p) - hasTargets %s&quot;, this, hasTargets ? &quot;TRUE&quot; : &quot;FALSE&quot;);
</ins><span class="cx"> 
</span><span class="cx">     m_hasPlaybackTargets = hasTargets;
</span><del>-    if (!m_targetAvailabilityChangedTimer.isActive())
-        m_targetAvailabilityChangedTimer.startOneShot(0);
</del><ins>+    m_targetAvailabilityChangedTimer.startOneShot(0);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool HTMLMediaSession::canPlayToWirelessPlaybackTarget() const
</span><span class="lines">@@ -330,6 +329,7 @@
</span><span class="cx"> 
</span><span class="cx"> void HTMLMediaSession::setShouldPlayToPlaybackTarget(bool shouldPlay)
</span><span class="cx"> {
</span><ins>+    LOG(Media, &quot;HTMLMediaSession::setShouldPlayToPlaybackTarget - shouldPlay %s&quot;, shouldPlay ? &quot;TRUE&quot; : &quot;FALSE&quot;);
</ins><span class="cx">     m_shouldPlayToPlaybackTarget = shouldPlay;
</span><span class="cx">     client().setShouldPlayToPlaybackTarget(shouldPlay);
</span><span class="cx"> }
</span><span class="lines">@@ -386,7 +386,7 @@
</span><span class="cx">     if (m_playbackTarget)
</span><span class="cx">         client().setWirelessPlaybackTarget(*m_playbackTarget.copyRef());
</span><span class="cx">     if (m_shouldPlayToPlaybackTarget)
</span><del>-        client().setShouldPlayToPlaybackTarget(m_shouldPlayToPlaybackTarget);
</del><ins>+        client().setShouldPlayToPlaybackTarget(true);
</ins><span class="cx"> #else
</span><span class="cx">     UNUSED_PARAM(element);
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaPlaybackTargetPickerMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm (184203 => 184204)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm        2015-05-12 16:25:32 UTC (rev 184203)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlaybackTargetPickerMac.mm        2015-05-12 16:41:48 UTC (rev 184204)
</span><span class="lines">@@ -130,23 +130,22 @@
</span><span class="cx">     return m_outputDeviceMenuController.get();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-#pragma clang diagnostic push
-#pragma clang diagnostic ignored &quot;-Wdeprecated-declarations&quot;
</del><span class="cx"> void MediaPlaybackTargetPickerMac::showPlaybackTargetPicker(const FloatRect&amp; location, bool checkActiveRoute)
</span><span class="cx"> {
</span><span class="cx">     if (!m_client || m_showingMenu)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_showingMenu = true;
</del><span class="cx">     AVOutputDeviceMenuControllerType *picker = devicePicker();
</span><del>-    if ([picker respondsToSelector:@selector(showMenuForRect:appearanceName:allowReselectionOfSelectedOutputDevice:)]) {
-        if ([picker showMenuForRect:location appearanceName:NSAppearanceNameVibrantLight allowReselectionOfSelectedOutputDevice:!checkActiveRoute])
</del><ins>+    if (![picker respondsToSelector:@selector(showMenuForRect:appearanceName:allowReselectionOfSelectedOutputDevice:)])
+        return;
+
+    m_showingMenu = true;
+    if ([picker showMenuForRect:location appearanceName:NSAppearanceNameVibrantLight allowReselectionOfSelectedOutputDevice:!checkActiveRoute]) {
+        if (!checkActiveRoute)
</ins><span class="cx">             currentDeviceDidChange();
</span><del>-    } else
-        [picker showMenuForRect:location appearanceName:NSAppearanceNameVibrantLight];
</del><ins>+    }
</ins><span class="cx">     m_showingMenu = false;
</span><span class="cx"> }
</span><del>-#pragma clang diagnostic pop
</del><span class="cx"> 
</span><span class="cx"> void MediaPlaybackTargetPickerMac::addPendingAction(PendingActionFlags action)
</span><span class="cx"> {
</span></span></pre>
</div>
</div>

</body>
</html>