<!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>[205938] trunk</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/205938">205938</a></dd>
<dt>Author</dt> <dd>wenson_hsieh@apple.com</dd>
<dt>Date</dt> <dd>2016-09-14 16:51:51 -0700 (Wed, 14 Sep 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Media controls behave strangely when changing media sources
https://bugs.webkit.org/show_bug.cgi?id=161914
&lt;rdar://problem/28227805&gt;

Reviewed by Tim Horton.

Source/WebCore:

Addresses media controls flickering while changing the source of a media element. To accomplish this, we make
the following changes to the media controls main content heuristic:

- Prevent elements that are not mostly within the mainframe rect (or elements with empty rects) from showing
  media controls. Many websites that rely on same document navigation will move videos offscreen when navigating
  to a section of their site that does not play media. Without this check, we would not know to hide a video
  element on certain popular websites that use this technique, since the video has been interacted with in the
  past.

- Rather than check whether a media element currently has video/audio sources, check whether it has ever had
  audio. Many websites will use the same media element across different videos and change only the source, and
  we should not prevent a media element from having media controls on grounds of having no audio or video in
  this case.

- Rather than add user gesture and playback behavior restrictions before dispatching an ended event, add only
  the gesture restriction immediately, and add the playback restriction after waiting for a grace period only if
  the user has not interacted with the video since ending, and the video is not currently playing or about to
  play. This gives the user a chance to interact with the controls when a video ends, but also allows the page
  to load or begin playing a new video with the same media element without thrashing media control state.

Adds 3 new API tests.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement):
(WebCore::HTMLMediaElement::~HTMLMediaElement):
(WebCore::HTMLMediaElement::mediaPlayerActiveSourceBuffersChanged):
(WebCore::HTMLMediaElement::seekWithTolerance):
(WebCore::HTMLMediaElement::beginScrubbing):
(WebCore::HTMLMediaElement::addBehaviorRestrictionsOnEndIfNecessary):
(WebCore::HTMLMediaElement::mediaPlayerCharacteristicChanged):
(WebCore::HTMLMediaElement::playbackControlsManagerBehaviorRestrictionsTimerFired):
* html/HTMLMediaElement.h:
(WebCore::HTMLMediaElement::hasEverHadAudio):
(WebCore::HTMLMediaElement::hasEverHadVideo):
* html/MediaElementSession.cpp:
(WebCore::MediaElementSession::canShowControlsManager):
(WebCore::isElementRectMostlyInMainFrame):
* platform/graphics/MediaPlayer.h:
(WebCore::MediaPlayerClient::mediaPlayerActiveSourceBuffersChanged):
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::notifyActiveSourceBuffersChanged):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::notifyActiveSourceBuffersChanged):
* platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
(WebCore::MediaSourcePrivateAVFObjC::removeSourceBuffer):
(WebCore::MediaSourcePrivateAVFObjC::sourceBufferPrivateDidChangeActiveState):

Source/WebKit2:

Allows a web page to have an active video for a media control manager even if no audio or video is currently
being produced. This is because the media element may be in a state where it is changing its source and does not
currently have a video or audio track.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::hasActiveVideoForControlsManager):

Tools:

Adds three new unit tests verifying that media controls remain stable during common `src` change scenarios. Also
tweaks an existing test to account for new `ended` behavior.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKit2Cocoa/VideoControlsManager.mm:
(-[VideoControlsManagerTestWebView waitForMediaControlsToShow]):
(-[VideoControlsManagerTestWebView waitForMediaControlsToHide]):
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-click.html: Added.
* TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-end.html: Added.
* TestWebKitAPI/Tests/WebKit2Cocoa/large-video-offscreen.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</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="#trunkSourceWebCorehtmlMediaElementSessioncpp">trunk/Source/WebCore/html/MediaElementSession.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsMediaPlayerh">trunk/Source/WebCore/platform/graphics/MediaPlayer.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsMediaPlayerPrivateh">trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaPlayerPrivateMediaSourceAVFObjCh">trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaPlayerPrivateMediaSourceAVFObjCmm">trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaSourcePrivateAVFObjCmm">trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebPageProxycpp">trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestWebKitAPIxcodeprojprojectpbxproj">trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2CocoaVideoControlsManagermm">trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/VideoControlsManager.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2Cocoachangevideosourceonclickhtml">trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-click.html</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2Cocoachangevideosourceonendhtml">trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-end.html</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2Cocoalargevideooffscreenhtml">trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/large-video-offscreen.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/ChangeLog        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -1,3 +1,59 @@
</span><ins>+2016-09-14  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Media controls behave strangely when changing media sources
+        https://bugs.webkit.org/show_bug.cgi?id=161914
+        &lt;rdar://problem/28227805&gt;
+
+        Reviewed by Tim Horton.
+
+        Addresses media controls flickering while changing the source of a media element. To accomplish this, we make
+        the following changes to the media controls main content heuristic:
+
+        - Prevent elements that are not mostly within the mainframe rect (or elements with empty rects) from showing
+          media controls. Many websites that rely on same document navigation will move videos offscreen when navigating
+          to a section of their site that does not play media. Without this check, we would not know to hide a video
+          element on certain popular websites that use this technique, since the video has been interacted with in the
+          past.
+
+        - Rather than check whether a media element currently has video/audio sources, check whether it has ever had
+          audio. Many websites will use the same media element across different videos and change only the source, and
+          we should not prevent a media element from having media controls on grounds of having no audio or video in
+          this case.
+
+        - Rather than add user gesture and playback behavior restrictions before dispatching an ended event, add only
+          the gesture restriction immediately, and add the playback restriction after waiting for a grace period only if
+          the user has not interacted with the video since ending, and the video is not currently playing or about to
+          play. This gives the user a chance to interact with the controls when a video ends, but also allows the page
+          to load or begin playing a new video with the same media element without thrashing media control state.
+
+        Adds 3 new API tests.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement):
+        (WebCore::HTMLMediaElement::~HTMLMediaElement):
+        (WebCore::HTMLMediaElement::mediaPlayerActiveSourceBuffersChanged):
+        (WebCore::HTMLMediaElement::seekWithTolerance):
+        (WebCore::HTMLMediaElement::beginScrubbing):
+        (WebCore::HTMLMediaElement::addBehaviorRestrictionsOnEndIfNecessary):
+        (WebCore::HTMLMediaElement::mediaPlayerCharacteristicChanged):
+        (WebCore::HTMLMediaElement::playbackControlsManagerBehaviorRestrictionsTimerFired):
+        * html/HTMLMediaElement.h:
+        (WebCore::HTMLMediaElement::hasEverHadAudio):
+        (WebCore::HTMLMediaElement::hasEverHadVideo):
+        * html/MediaElementSession.cpp:
+        (WebCore::MediaElementSession::canShowControlsManager):
+        (WebCore::isElementRectMostlyInMainFrame):
+        * platform/graphics/MediaPlayer.h:
+        (WebCore::MediaPlayerClient::mediaPlayerActiveSourceBuffersChanged):
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::notifyActiveSourceBuffersChanged):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::notifyActiveSourceBuffersChanged):
+        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
+        (WebCore::MediaSourcePrivateAVFObjC::removeSourceBuffer):
+        (WebCore::MediaSourcePrivateAVFObjC::sourceBufferPrivateDidChangeActiveState):
+
</ins><span class="cx"> 2016-09-14  Eric Carlson  &lt;eric.carlson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [MediaStream] Minor cleanup
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.cpp        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -163,6 +163,8 @@
</span><span class="cx"> static const double ScanRepeatDelay = 1.5;
</span><span class="cx"> static const double ScanMaximumRate = 8;
</span><span class="cx"> 
</span><ins>+static const double HideMediaControlsAfterEndedDelay = 6;
+
</ins><span class="cx"> static void setFlags(unsigned&amp; value, unsigned flags)
</span><span class="cx"> {
</span><span class="cx">     value |= flags;
</span><span class="lines">@@ -426,6 +428,7 @@
</span><span class="cx">     , m_progressEventTimer(*this, &amp;HTMLMediaElement::progressEventTimerFired)
</span><span class="cx">     , m_playbackProgressTimer(*this, &amp;HTMLMediaElement::playbackProgressTimerFired)
</span><span class="cx">     , m_scanTimer(*this, &amp;HTMLMediaElement::scanTimerFired)
</span><ins>+    , m_playbackControlsManagerBehaviorRestrictionsTimer(*this, &amp;HTMLMediaElement::playbackControlsManagerBehaviorRestrictionsTimerFired)
</ins><span class="cx">     , m_playedTimeRanges()
</span><span class="cx">     , m_asyncEventQueue(*this)
</span><span class="cx">     , m_requestedPlaybackRate(1)
</span><span class="lines">@@ -482,6 +485,8 @@
</span><span class="cx">     , m_creatingControls(false)
</span><span class="cx">     , m_receivedLayoutSizeChanged(false)
</span><span class="cx">     , m_hasEverNotifiedAboutPlaying(false)
</span><ins>+    , m_hasEverHadAudio(false)
+    , m_hasEverHadVideo(false)
</ins><span class="cx"> #if ENABLE(MEDIA_CONTROLS_SCRIPT)
</span><span class="cx">     , m_mediaControlsDependOnPageScaleFactor(false)
</span><span class="cx">     , m_haveSetUpCaptionContainer(false)
</span><span class="lines">@@ -630,6 +635,7 @@
</span><span class="cx">     m_promiseTaskQueue.close();
</span><span class="cx">     m_pauseAfterDetachedTaskQueue.close();
</span><span class="cx">     m_updatePlaybackControlsManagerQueue.close();
</span><ins>+    m_playbackControlsManagerBehaviorRestrictionsQueue.close();
</ins><span class="cx"> 
</span><span class="cx">     m_completelyLoaded = true;
</span><span class="cx"> 
</span><span class="lines">@@ -972,6 +978,12 @@
</span><span class="cx">     m_pendingActionTimer.startOneShot(0);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void HTMLMediaElement::mediaPlayerActiveSourceBuffersChanged(const MediaPlayer*)
+{
+    m_hasEverHadAudio |= hasAudio();
+    m_hasEverHadVideo |= hasVideo();
+}
+
</ins><span class="cx"> void HTMLMediaElement::scheduleEvent(const AtomicString&amp; eventName)
</span><span class="cx"> {
</span><span class="cx"> #if LOG_MEDIA_EVENTS
</span><span class="lines">@@ -2675,6 +2687,9 @@
</span><span class="cx">         m_seekTaskQueue.enqueueTask(std::bind(&amp;HTMLMediaElement::seekTask, this));
</span><span class="cx">     } else
</span><span class="cx">         seekTask();
</span><ins>+
+    if (ScriptController::processingUserGestureForMedia())
+        m_mediaSession-&gt;removeBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLMediaElement::seekTask()
</span><span class="lines">@@ -3491,6 +3506,8 @@
</span><span class="cx">             setPausedInternal(true);
</span><span class="cx">         }
</span><span class="cx">     }
</span><ins>+
+    m_mediaSession-&gt;removeBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLMediaElement::endScrubbing()
</span><span class="lines">@@ -4537,7 +4554,9 @@
</span><span class="cx">     if (isFullscreen())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_mediaSession-&gt;addBehaviorRestriction(MediaElementSession::RequirePlaybackToControlControlsManager | MediaElementSession::RequireUserGestureToControlControlsManager);
</del><ins>+    m_mediaSession-&gt;addBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
+    m_playbackControlsManagerBehaviorRestrictionsTimer.stop();
+    m_playbackControlsManagerBehaviorRestrictionsTimer.startOneShot(HideMediaControlsAfterEndedDelay);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
</span><span class="lines">@@ -4772,6 +4791,9 @@
</span><span class="cx">     document().updateIsPlayingMedia();
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    m_hasEverHadAudio |= hasAudio();
+    m_hasEverHadVideo |= hasVideo();
+
</ins><span class="cx">     endProcessingMediaPlayerCallback();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -7301,6 +7323,25 @@
</span><span class="cx">         m_updatePlaybackControlsManagerQueue.enqueueTask(std::bind(&amp;HTMLMediaElement::updatePlaybackControlsManager, this));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void HTMLMediaElement::playbackControlsManagerBehaviorRestrictionsTimerFired()
+{
+    if (m_playbackControlsManagerBehaviorRestrictionsQueue.hasPendingTasks())
+        return;
+
+    if (!m_mediaSession-&gt;hasBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager))
+        return;
+
+    RefPtr&lt;HTMLMediaElement&gt; protectedThis(this);
+    m_playbackControlsManagerBehaviorRestrictionsQueue.enqueueTask([protectedThis] () {
+        MediaElementSession* mediaElementSession = protectedThis-&gt;m_mediaSession.get();
+        if (protectedThis-&gt;isPlaying() || mediaElementSession-&gt;state() == PlatformMediaSession::Autoplaying || mediaElementSession-&gt;state() == PlatformMediaSession::Playing)
+            return;
+
+        mediaElementSession-&gt;addBehaviorRestriction(MediaElementSession::RequirePlaybackToControlControlsManager);
+        protectedThis-&gt;scheduleUpdatePlaybackControlsManager();
+    });
+}
+
</ins><span class="cx"> bool HTMLMediaElement::shouldOverrideBackgroundLoadingRestriction() const
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(WIRELESS_PLAYBACK_TARGET)
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.h        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -473,6 +473,9 @@
</span><span class="cx">     bool hasEverNotifiedAboutPlaying() const;
</span><span class="cx">     void setShouldDelayLoadEvent(bool);
</span><span class="cx"> 
</span><ins>+    bool hasEverHadAudio() const { return m_hasEverHadAudio; }
+    bool hasEverHadVideo() const { return m_hasEverHadVideo; }
+
</ins><span class="cx"> protected:
</span><span class="cx">     HTMLMediaElement(const QualifiedName&amp;, Document&amp;, bool createdByParser);
</span><span class="cx">     virtual ~HTMLMediaElement();
</span><span class="lines">@@ -620,6 +623,8 @@
</span><span class="cx">     GraphicsDeviceAdapter* mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const override;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    void mediaPlayerActiveSourceBuffersChanged(const MediaPlayer*) override;
+
</ins><span class="cx">     bool mediaPlayerShouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge&amp;) override;
</span><span class="cx">     void mediaPlayerHandlePlaybackCommand(PlatformMediaSession::RemoteControlCommandType command) override { didReceiveRemoteControlCommand(command, nullptr); }
</span><span class="cx">     String mediaPlayerSourceApplicationIdentifier() const override;
</span><span class="lines">@@ -791,6 +796,7 @@
</span><span class="cx">     void pauseAfterDetachedTask();
</span><span class="cx">     void updatePlaybackControlsManager();
</span><span class="cx">     void scheduleUpdatePlaybackControlsManager();
</span><ins>+    void playbackControlsManagerBehaviorRestrictionsTimerFired();
</ins><span class="cx"> 
</span><span class="cx">     void updateRenderer();
</span><span class="cx"> 
</span><span class="lines">@@ -804,6 +810,7 @@
</span><span class="cx">     Timer m_progressEventTimer;
</span><span class="cx">     Timer m_playbackProgressTimer;
</span><span class="cx">     Timer m_scanTimer;
</span><ins>+    Timer m_playbackControlsManagerBehaviorRestrictionsTimer;
</ins><span class="cx">     GenericTaskQueue&lt;Timer&gt; m_seekTaskQueue;
</span><span class="cx">     GenericTaskQueue&lt;Timer&gt; m_resizeTaskQueue;
</span><span class="cx">     GenericTaskQueue&lt;Timer&gt; m_shadowDOMTaskQueue;
</span><span class="lines">@@ -810,6 +817,7 @@
</span><span class="cx">     GenericTaskQueue&lt;Timer&gt; m_promiseTaskQueue;
</span><span class="cx">     GenericTaskQueue&lt;Timer&gt; m_pauseAfterDetachedTaskQueue;
</span><span class="cx">     GenericTaskQueue&lt;Timer&gt; m_updatePlaybackControlsManagerQueue;
</span><ins>+    GenericTaskQueue&lt;Timer&gt; m_playbackControlsManagerBehaviorRestrictionsQueue;
</ins><span class="cx">     RefPtr&lt;TimeRanges&gt; m_playedTimeRanges;
</span><span class="cx">     GenericEventQueue m_asyncEventQueue;
</span><span class="cx"> 
</span><span class="lines">@@ -946,6 +954,9 @@
</span><span class="cx">     bool m_receivedLayoutSizeChanged : 1;
</span><span class="cx">     bool m_hasEverNotifiedAboutPlaying : 1;
</span><span class="cx"> 
</span><ins>+    bool m_hasEverHadAudio : 1;
+    bool m_hasEverHadVideo : 1;
+
</ins><span class="cx"> #if ENABLE(MEDIA_CONTROLS_SCRIPT)
</span><span class="cx">     bool m_mediaControlsDependOnPageScaleFactor : 1;
</span><span class="cx">     bool m_haveSetUpCaptionContainer : 1;
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlMediaElementSessioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/MediaElementSession.cpp (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/MediaElementSession.cpp        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/html/MediaElementSession.cpp        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -59,6 +59,7 @@
</span><span class="cx"> 
</span><span class="cx"> static const double elementMainContentCheckInterval = .250;
</span><span class="cx"> 
</span><ins>+static bool isElementRectMostlyInMainFrame(const HTMLMediaElement&amp;);
</ins><span class="cx"> static bool isElementLargeEnoughForMainContent(const HTMLMediaElement&amp;, MediaSessionMainContentPurpose);
</span><span class="cx"> 
</span><span class="cx"> #if !LOG_DISABLED
</span><span class="lines">@@ -222,7 +223,12 @@
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (!m_element.hasAudio()) {
</del><ins>+    if (!isElementRectMostlyInMainFrame(m_element)) {
+        LOG(Media, &quot;MediaElementSession::canShowControlsManager - returning FALSE: Not in main frame&quot;);
+        return false;
+    }
+
+    if (!m_element.hasAudio() &amp;&amp; !m_element.hasEverHadAudio()) {
</ins><span class="cx">         LOG(Media, &quot;MediaElementSession::canShowControlsManager - returning FALSE: No audio&quot;);
</span><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="lines">@@ -268,7 +274,7 @@
</span><span class="cx">             return false;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (!m_element.hasVideo()) {
</del><ins>+        if (!m_element.hasVideo() &amp;&amp; !m_element.hasEverHadVideo()) {
</ins><span class="cx">             LOG(Media, &quot;MediaElementSession::canShowControlsManager - returning FALSE: No video&quot;);
</span><span class="cx">             return false;
</span><span class="cx">         }
</span><span class="lines">@@ -630,6 +636,27 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static bool isElementRectMostlyInMainFrame(const HTMLMediaElement&amp; element)
+{
+    if (!element.renderer())
+        return false;
+
+    auto* documentFrame = element.document().frame();
+    if (!documentFrame)
+        return false;
+
+    auto mainFrameView = documentFrame-&gt;mainFrame().view();
+    if (!mainFrameView)
+        return false;
+
+    IntRect mainFrameRectAdjustedForScrollPosition = IntRect(-mainFrameView-&gt;documentScrollPositionRelativeToViewOrigin(), mainFrameView-&gt;contentsSize());
+    IntRect elementRectInMainFrame = element.clientRect();
+    unsigned int totalElementArea = elementRectInMainFrame.area();
+    elementRectInMainFrame.intersect(mainFrameRectAdjustedForScrollPosition);
+
+    return elementRectInMainFrame.area() &gt; totalElementArea / 2;
+}
+
</ins><span class="cx"> static bool isElementLargeRelativeToMainFrame(const HTMLMediaElement&amp; element)
</span><span class="cx"> {
</span><span class="cx">     static const double minimumPercentageOfMainFrameAreaForMainContent = 0.9;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsMediaPlayerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/MediaPlayer.h (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/MediaPlayer.h        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/platform/graphics/MediaPlayer.h        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -196,6 +196,8 @@
</span><span class="cx">     // availability of the platformLayer().
</span><span class="cx">     virtual void mediaPlayerRenderingModeChanged(MediaPlayer*) { }
</span><span class="cx"> 
</span><ins>+    virtual void mediaPlayerActiveSourceBuffersChanged(const MediaPlayer*) { }
+
</ins><span class="cx"> #if PLATFORM(WIN) &amp;&amp; USE(AVFOUNDATION)
</span><span class="cx">     virtual GraphicsDeviceAdapter* mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const { return 0; }
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsMediaPlayerPrivateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/platform/graphics/MediaPlayerPrivate.h        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -275,6 +275,8 @@
</span><span class="cx"> #if ENABLE(AVF_CAPTIONS)
</span><span class="cx">     virtual void notifyTrackModeChanged() { }
</span><span class="cx"> #endif
</span><ins>+
+    virtual void notifyActiveSourceBuffersChanged() { }
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaPlayerPrivateMediaSourceAVFObjCh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -165,6 +165,7 @@
</span><span class="cx">     bool supportsAcceleratedRendering() const override;
</span><span class="cx">     // called when the rendering system flips the into or out of accelerated rendering mode.
</span><span class="cx">     void acceleratedRenderingStateChanged() override;
</span><ins>+    void notifyActiveSourceBuffersChanged() override;
</ins><span class="cx"> 
</span><span class="cx">     MediaPlayer::MovieLoadType movieLoadType() const override;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaPlayerPrivateMediaSourceAVFObjCmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -556,6 +556,11 @@
</span><span class="cx">         destroyLayer();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void MediaPlayerPrivateMediaSourceAVFObjC::notifyActiveSourceBuffersChanged()
+{
+    m_player-&gt;client().mediaPlayerActiveSourceBuffersChanged(m_player);
+}
+
</ins><span class="cx"> MediaPlayer::MovieLoadType MediaPlayerPrivateMediaSourceAVFObjC::movieLoadType() const
</span><span class="cx"> {
</span><span class="cx">     return MediaPlayer::StoredStream;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsavfoundationobjcMediaSourcePrivateAVFObjCmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -83,8 +83,10 @@
</span><span class="cx">     ASSERT(m_sourceBuffers.contains(buffer));
</span><span class="cx"> 
</span><span class="cx">     size_t pos = m_activeSourceBuffers.find(buffer);
</span><del>-    if (pos != notFound)
</del><ins>+    if (pos != notFound) {
</ins><span class="cx">         m_activeSourceBuffers.remove(pos);
</span><ins>+        m_player-&gt;notifyActiveSourceBuffersChanged();
+    }
</ins><span class="cx"> 
</span><span class="cx">     pos = m_sourceBuffers.find(buffer);
</span><span class="cx">     m_sourceBuffers[pos]-&gt;clearMediaSource();
</span><span class="lines">@@ -141,13 +143,17 @@
</span><span class="cx"> 
</span><span class="cx"> void MediaSourcePrivateAVFObjC::sourceBufferPrivateDidChangeActiveState(SourceBufferPrivateAVFObjC* buffer, bool active)
</span><span class="cx"> {
</span><del>-    if (active &amp;&amp; !m_activeSourceBuffers.contains(buffer))
</del><ins>+    if (active &amp;&amp; !m_activeSourceBuffers.contains(buffer)) {
</ins><span class="cx">         m_activeSourceBuffers.append(buffer);
</span><ins>+        m_player-&gt;notifyActiveSourceBuffersChanged();
+    }
</ins><span class="cx"> 
</span><span class="cx">     if (!active) {
</span><span class="cx">         size_t position = m_activeSourceBuffers.find(buffer);
</span><del>-        if (position != notFound)
</del><ins>+        if (position != notFound) {
</ins><span class="cx">             m_activeSourceBuffers.remove(position);
</span><ins>+            m_player-&gt;notifyActiveSourceBuffersChanged();
+        }
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebKit2/ChangeLog        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -1,3 +1,18 @@
</span><ins>+2016-09-14  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Media controls behave strangely when changing media sources
+        https://bugs.webkit.org/show_bug.cgi?id=161914
+        &lt;rdar://problem/28227805&gt;
+
+        Reviewed by Tim Horton.
+
+        Allows a web page to have an active video for a media control manager even if no audio or video is currently
+        being produced. This is because the media element may be in a state where it is changing its source and does not
+        currently have a video or audio track.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::hasActiveVideoForControlsManager):
+
</ins><span class="cx"> 2016-09-14  Beth Dakin  &lt;bdakin@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add needsPlainTextQuirk and send it to the UIProcess
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebPageProxycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -6329,7 +6329,7 @@
</span><span class="cx"> bool WebPageProxy::hasActiveVideoForControlsManager() const
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(VIDEO_PRESENTATION_MODE)
</span><del>-    return m_playbackSessionManager &amp;&amp; m_playbackSessionManager-&gt;controlsManagerInterface() &amp;&amp; m_mediaState &amp; MediaProducer::HasAudioOrVideo;
</del><ins>+    return m_playbackSessionManager &amp;&amp; m_playbackSessionManager-&gt;controlsManagerInterface();
</ins><span class="cx"> #else
</span><span class="cx">     return false;
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Tools/ChangeLog        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -1,3 +1,23 @@
</span><ins>+2016-09-14  Wenson Hsieh  &lt;wenson_hsieh@apple.com&gt;
+
+        Media controls behave strangely when changing media sources
+        https://bugs.webkit.org/show_bug.cgi?id=161914
+        &lt;rdar://problem/28227805&gt;
+
+        Reviewed by Tim Horton.
+
+        Adds three new unit tests verifying that media controls remain stable during common `src` change scenarios. Also
+        tweaks an existing test to account for new `ended` behavior.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKit2Cocoa/VideoControlsManager.mm:
+        (-[VideoControlsManagerTestWebView waitForMediaControlsToShow]):
+        (-[VideoControlsManagerTestWebView waitForMediaControlsToHide]):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-click.html: Added.
+        * TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-end.html: Added.
+        * TestWebKitAPI/Tests/WebKit2Cocoa/large-video-offscreen.html: Added.
+
</ins><span class="cx"> 2016-09-14  Jonathan Bedard  &lt;jbedard@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix mastercfg_unittest
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestWebKitAPIxcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -75,6 +75,9 @@
</span><span class="cx">                 2E691AF31D79E75E00129407 /* large-video-playing-scroll-away.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E691AF21D79E75400129407 /* large-video-playing-scroll-away.html */; };
</span><span class="cx">                 2E7765CD16C4D80A00BA2BB1 /* mainIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E7765CC16C4D80A00BA2BB1 /* mainIOS.mm */; };
</span><span class="cx">                 2E7765CF16C4D81100BA2BB1 /* mainMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E7765CE16C4D81100BA2BB1 /* mainMac.mm */; };
</span><ins>+                2EFF06C31D88621E0004BB30 /* large-video-offscreen.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2EFF06C21D8862120004BB30 /* large-video-offscreen.html */; };
+                2EFF06C51D8867760004BB30 /* change-video-source-on-click.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2EFF06C41D8867700004BB30 /* change-video-source-on-click.html */; };
+                2EFF06C71D886A580004BB30 /* change-video-source-on-end.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2EFF06C61D886A560004BB30 /* change-video-source-on-end.html */; };
</ins><span class="cx">                 33BE5AF9137B5AAE00705813 /* MouseMoveAfterCrash_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33BE5AF8137B5AAE00705813 /* MouseMoveAfterCrash_Bundle.cpp */; };
</span><span class="cx">                 33DC8912141955FE00747EF7 /* simple-iframe.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 33DC890E1419539300747EF7 /* simple-iframe.html */; };
</span><span class="cx">                 33DC89141419579F00747EF7 /* LoadCanceledNoServerRedirectCallback_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33DC89131419579F00747EF7 /* LoadCanceledNoServerRedirectCallback_Bundle.cpp */; };
</span><span class="lines">@@ -538,6 +541,9 @@
</span><span class="cx">                         dstPath = TestWebKitAPI.resources;
</span><span class="cx">                         dstSubfolderSpec = 7;
</span><span class="cx">                         files = (
</span><ins>+                                2EFF06C71D886A580004BB30 /* change-video-source-on-end.html in Copy Resources */,
+                                2EFF06C51D8867760004BB30 /* change-video-source-on-click.html in Copy Resources */,
+                                2EFF06C31D88621E0004BB30 /* large-video-offscreen.html in Copy Resources */,
</ins><span class="cx">                                 2E131C181D83A98A001BA36C /* wide-autoplaying-video-with-audio.html in Copy Resources */,
</span><span class="cx">                                 2E54F40D1D7BC84200921ADF /* large-video-mutes-onplaying.html in Copy Resources */,
</span><span class="cx">                                 2E691AF31D79E75E00129407 /* large-video-playing-scroll-away.html in Copy Resources */,
</span><span class="lines">@@ -780,6 +786,9 @@
</span><span class="cx">                 2E691AF21D79E75400129407 /* large-video-playing-scroll-away.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = &quot;large-video-playing-scroll-away.html&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 2E7765CC16C4D80A00BA2BB1 /* mainIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = mainIOS.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 2E7765CE16C4D81100BA2BB1 /* mainMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = mainMac.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                2EFF06C21D8862120004BB30 /* large-video-offscreen.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = &quot;large-video-offscreen.html&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
+                2EFF06C41D8867700004BB30 /* change-video-source-on-click.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = &quot;change-video-source-on-click.html&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
+                2EFF06C61D886A560004BB30 /* change-video-source-on-end.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = &quot;change-video-source-on-end.html&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 333B9CE11277F23100FEFCE3 /* PreventEmptyUserAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PreventEmptyUserAgent.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 33BE5AF4137B5A6C00705813 /* MouseMoveAfterCrash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MouseMoveAfterCrash.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 33BE5AF8137B5AAE00705813 /* MouseMoveAfterCrash_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MouseMoveAfterCrash_Bundle.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -1455,6 +1464,9 @@
</span><span class="cx">                 A16F66B81C40E9E100BD4D24 /* Resources */ = {
</span><span class="cx">                         isa = PBXGroup;
</span><span class="cx">                         children = (
</span><ins>+                                2EFF06C61D886A560004BB30 /* change-video-source-on-end.html */,
+                                2EFF06C41D8867700004BB30 /* change-video-source-on-click.html */,
+                                2EFF06C21D8862120004BB30 /* large-video-offscreen.html */,
</ins><span class="cx">                                 2E131C171D83A97E001BA36C /* wide-autoplaying-video-with-audio.html */,
</span><span class="cx">                                 2E54F40C1D7BC83900921ADF /* large-video-mutes-onplaying.html */,
</span><span class="cx">                                 2E691AF21D79E75400129407 /* large-video-playing-scroll-away.html */,
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2CocoaVideoControlsManagermm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/VideoControlsManager.mm (205937 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/VideoControlsManager.mm        2016-09-14 23:17:59 UTC (rev 205937)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/VideoControlsManager.mm        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -119,6 +119,18 @@
</span><span class="cx">     TestWebKitAPI::Util::run(&amp;doneWaiting);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)waitForMediaControlsToShow
+{
+    while (![self _hasActiveVideoForControlsManager])
+        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
+}
+
+- (void)waitForMediaControlsToHide
+{
+    while ([self _hasActiveVideoForControlsManager])
+        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
+}
+
</ins><span class="cx"> - (void)performAfterReceivingMessage:(NSString *)message action:(dispatch_block_t)action
</span><span class="cx"> {
</span><span class="cx">     RetainPtr&lt;MessageHandler&gt; handler = adoptNS([[MessageHandler alloc] initWithMessage:message handler:action]);
</span><span class="lines">@@ -319,9 +331,13 @@
</span><span class="cx"> {
</span><span class="cx">     RetainPtr&lt;VideoControlsManagerTestWebView*&gt; webView = setUpWebViewForTestingVideoControlsManager(NSMakeRect(0, 0, 100, 100));
</span><span class="cx"> 
</span><del>-    // Since the video has ended, the expectation is NO even if the page programmatically seeks to the beginning.
</del><span class="cx">     [webView loadTestPageNamed:@&quot;large-video-seek-after-ending&quot;];
</span><del>-    [webView expectControlsManager:NO afterReceivingMessage:@&quot;ended&quot;];
</del><ins>+
+    // Immediately after ending, the controls should still be present.
+    [webView expectControlsManager:YES afterReceivingMessage:@&quot;ended&quot;];
+
+    // At some point in the future, they should automatically hide.
+    [webView waitForMediaControlsToHide];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> TEST(VideoControlsManager, VideoControlsManagerLargeAutoplayingVideoSeeksAndPlaysAfterEnding)
</span><span class="lines">@@ -385,6 +401,33 @@
</span><span class="cx">     TestWebKitAPI::Util::run(&amp;finishedTest);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TEST(VideoControlsManager, VideoControlsManagerDoesNotShowMediaControlsForOffscreenVideo)
+{
+    RetainPtr&lt;VideoControlsManagerTestWebView*&gt; webView = setUpWebViewForTestingVideoControlsManager(NSMakeRect(0, 0, 1024, 768));
+
+    [webView loadTestPageNamed:@&quot;large-video-offscreen&quot;];
+    [webView expectControlsManager:NO afterReceivingMessage:@&quot;moved&quot;];
+}
+
+TEST(VideoControlsManager, VideoControlsManagerKeepsControlsStableDuringSrcChangeOnClick)
+{
+    RetainPtr&lt;VideoControlsManagerTestWebView*&gt; webView = setUpWebViewForTestingVideoControlsManager(NSMakeRect(0, 0, 800, 600));
+
+    [webView loadTestPageNamed:@&quot;change-video-source-on-click&quot;];
+    [webView waitForPageToLoadWithAutoplayingVideos:1];
+    [webView mouseDownAtPoint:NSMakePoint(400, 300)];
+
+    [webView expectControlsManager:YES afterReceivingMessage:@&quot;changed&quot;];
+}
+
+TEST(VideoControlsManager, VideoControlsManagerKeepsControlsStableDuringSrcChangeOnEnd)
+{
+    RetainPtr&lt;VideoControlsManagerTestWebView*&gt; webView = setUpWebViewForTestingVideoControlsManager(NSMakeRect(0, 0, 800, 600));
+
+    [webView loadTestPageNamed:@&quot;change-video-source-on-end&quot;];
+    [webView expectControlsManager:YES afterReceivingMessage:@&quot;changed&quot;];
+}
+
</ins><span class="cx"> TEST(VideoControlsManager, VideoControlsManagerSmallVideoInMediaDocument)
</span><span class="cx"> {
</span><span class="cx">     RetainPtr&lt;VideoControlsManagerTestWebView*&gt; webView = setUpWebViewForTestingVideoControlsManager(NSMakeRect(0, 0, 100, 100));
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2Cocoachangevideosourceonclickhtml"></a>
<div class="addfile"><h4>Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-click.html (0 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-click.html                                (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-click.html        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+&lt;head&gt;
+    &lt;style&gt;
+        video {
+            width: 800px;
+            height: 600px;
+        }
+    &lt;/style&gt;
+    &lt;script type=&quot;text/javascript&quot;&gt;
+        function changeSource() {
+            var video = document.querySelector(&quot;video&quot;);
+            video.src = &quot;large-video-with-audio.mp4&quot;;
+            video.play();
+            setTimeout(function() {
+                try {
+                    window.webkit.messageHandlers.playingHandler.postMessage(&quot;changed&quot;);
+                } catch(e) {
+                }
+            });
+        }
+
+        function handlePlaying() {
+            setTimeout(function() {
+                try {
+                    window.webkit.messageHandlers.playingHandler.postMessage(&quot;autoplayed&quot;);
+                } catch(e) {
+                }
+            }, 0);
+        }
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+    &lt;video autoplay onplaying=handlePlaying() onmousedown=changeSource() src=&quot;large-video-with-audio.mp4&quot;&gt;&lt;/video&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2Cocoachangevideosourceonendhtml"></a>
<div class="addfile"><h4>Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-end.html (0 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-end.html                                (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/change-video-source-on-end.html        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+&lt;head&gt;
+    &lt;style&gt;
+        video {
+            width: 800px;
+            height: 600px;
+        }
+    &lt;/style&gt;
+    &lt;script type=&quot;text/javascript&quot;&gt;
+        var hasBegunPlayingBefore = false;
+
+        function changeSource() {
+            var video = document.querySelector(&quot;video&quot;);
+            video.src = &quot;large-video-with-audio.mp4&quot;;
+            video.play();
+            setTimeout(function() {
+                try {
+                    window.webkit.messageHandlers.playingHandler.postMessage(&quot;changed&quot;);
+                } catch(e) {
+                }
+            });
+        }
+
+        function handlePlaying() {
+            if (hasBegunPlayingBefore)
+                return;
+
+            hasBegunPlayingBefore = true;
+            var video = document.querySelector(&quot;video&quot;);
+            video.currentTime = video.duration - 0.5;
+        }
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+    &lt;video autoplay onplaying=handlePlaying() src=&quot;large-video-with-audio.mp4&quot; onended=changeSource()&gt;&lt;/video&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2Cocoalargevideooffscreenhtml"></a>
<div class="addfile"><h4>Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/large-video-offscreen.html (0 => 205938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/large-video-offscreen.html                                (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/large-video-offscreen.html        2016-09-14 23:51:51 UTC (rev 205938)
</span><span class="lines">@@ -0,0 +1,29 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+    &lt;style&gt;
+        video {
+            width: 480px;
+            height: 320px;
+            position: absolute;
+        }
+        .offscreen {
+            top: -320px;
+        }
+    &lt;/style&gt;
+    &lt;script&gt;
+        function moveVideoOffscreen() {
+            document.querySelector(&quot;video&quot;).classList.add(&quot;offscreen&quot;);
+            setTimeout(function() {
+                try {
+                    window.webkit.messageHandlers.playingHandler.postMessage(&quot;moved&quot;);
+                } catch(e) {
+                }
+            }, 0);
+        }
+    &lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+    &lt;video autoplay onplaying=moveVideoOffscreen()&gt;&lt;source src=&quot;large-video-with-audio.mp4&quot;&gt;&lt;/video&gt;
+&lt;/body&gt;
+&lt;html&gt;
</ins></span></pre>
</div>
</div>

</body>
</html>