<!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>[209182] 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/209182">209182</a></dd>
<dt>Author</dt> <dd>graouts@webkit.org</dd>
<dt>Date</dt> <dd>2016-12-01 08:16:38 -0800 (Thu, 01 Dec 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Modern Media Controls] Provide a UI object to show a list of tracks
https://bugs.webkit.org/show_bug.cgi?id=165239

Reviewed by Dean Jackson.

We add a new TracksPanel object which we will be using to display a list of
audio and text tracks. The tracks panel can be shown by calling showTracksPanel()
on a MacOSMediaControls object and will be dismissed by hitting the Escape key
or mousing down outside of the panel's bounds. While the tracks panel is up,
arrows can be used to focus individual tracks which can be activated by either
pressing the Space bar or Enter key.

Activating a track will briefly animate its background to indicate selection and
dismissing the tracks panel is also animated with a quick fade-out animation.

Data for the tracks panel is provided by specifying a dataSource property and
implementing the required methods to provide the number of sections in the panel,
the number of tracks in each section, etc.

Tests: media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside.html
       media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key.html
       media/modern-media-controls/tracks-panel/tracks-panel-hide.html
       media/modern-media-controls/tracks-panel/tracks-panel-population.html
       media/modern-media-controls/tracks-panel/tracks-panel-right-x.html
       media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard.html
       media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse.html
       media/modern-media-controls/tracks-panel/tracks-panel.html

* Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css:
(.media-controls.mac.fullscreen):
(.media-controls.mac.fullscreen &gt; .controls-bar):
(.media-controls.mac.fullscreen .tracks-panel):
* Modules/modern-media-controls/controls/macos-inline-media-controls.css:
(.media-controls.mac.inline .tracks-panel):
* Modules/modern-media-controls/controls/macos-media-controls.js:
(MacOSMediaControls.prototype.showTracksPanel):
(MacOSMediaControls.prototype.hideTracksPanel):
(MacOSMediaControls):
* Modules/modern-media-controls/controls/media-controls.css:
(.media-controls):
* Modules/modern-media-controls/controls/placard.css:
(.placard):
* Modules/modern-media-controls/controls/status-label.css:
(.status-label):
* Modules/modern-media-controls/controls/tracks-panel.css: Added.
(.tracks-panel):
(.tracks-panel *):
(.tracks-panel.fade-out):
(.tracks-panel-section):
(.tracks-panel-section:first-of-type):
(.tracks-panel-section &gt; h3):
(.tracks-panel-section &gt; ul):
(.tracks-panel-section &gt; ul &gt; li):
(.tracks-panel-section &gt; ul &gt; li:focus):
(.tracks-panel-section &gt; ul &gt; li.selected:before):
(.tracks-panel-section &gt; ul &gt; li.animated):
(@keyframes tracks-panel-item-selection):
(22.22%):
* Modules/modern-media-controls/controls/tracks-panel.js: Added.
(TracksPanel.prototype.get presented):
(TracksPanel.prototype.presentInParent):
(TracksPanel.prototype.hide):
(TracksPanel.prototype.get rightX):
(TracksPanel.prototype.set rightX):
(TracksPanel.prototype.trackNodeSelectionAnimationDidEnd):
(TracksPanel.prototype.mouseMovedOverTrackNode):
(TracksPanel.prototype.mouseExitedTrackNode):
(TracksPanel.prototype.commitProperty):
(TracksPanel.prototype.handleEvent):
(TracksPanel.prototype._childrenFromDataSource.):
(TracksPanel.prototype._childrenFromDataSource):
(TracksPanel.prototype._handleMousedown):
(TracksPanel.prototype._handleKeydown):
(TracksPanel.prototype._dismiss):
(TracksPanel.prototype._focusTrackNode):
(TracksPanel.prototype._focusPreviousTrackNode):
(TracksPanel.prototype._focusNextTrackNode):
(TracksPanel.prototype._focusFirstTrackNode):
(TracksPanel.prototype._focusLastTrackNode):
(TrackNode):
(TrackNode.prototype.activate):
(TrackNode.prototype.handleEvent):
(TrackNode.prototype._animationDidEnd):
* Modules/modern-media-controls/js-files:
* WebCore.xcodeproj/project.pbxproj:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolsresourcesmediacontrolsloaderjs">trunk/LayoutTests/media/modern-media-controls/resources/media-controls-loader.js</a></li>
<li><a href="#trunkLayoutTestsplatformiossimulatorTestExpectations">trunk/LayoutTests/platform/ios-simulator/TestExpectations</a></li>
<li><a href="#trunkLayoutTestsplatformmacTestExpectations">trunk/LayoutTests/platform/mac/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsmacosfullscreenmediacontrolscss">trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsmacosinlinemediacontrolscss">trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsmacosmediacontrolsjs">trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsmediacontrolscss">trunk/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsplacardcss">trunk/Source/WebCore/Modules/modern-media-controls/controls/placard.css</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsstatuslabelcss">trunk/Source/WebCore/Modules/modern-media-controls/controls/status-label.css</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolsjsfiles">trunk/Source/WebCore/Modules/modern-media-controls/js-files</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/LayoutTests/media/modern-media-controls/tracks-panel/</li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelexpectedtxt">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideclickoutsideexpectedtxt">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideclickoutsidehtml">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideesckeyexpectedtxt">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideesckeyhtml">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideexpectedtxt">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhidehtml">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelpopulationexpectedtxt">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-population-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelpopulationhtml">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-population.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelrightxexpectedtxt">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-right-x-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelrightxhtml">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-right-x.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelselecttrackwithkeyboardexpectedtxt">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelselecttrackwithkeyboardhtml">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelselecttrackwithmouseexpectedtxt">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelselecttrackwithmousehtml">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhtml">trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel.html</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolstrackspanelcss">trunk/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolstrackspaneljs">trunk/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/LayoutTests/ChangeLog        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -1,3 +1,32 @@
</span><ins>+2016-12-01  Antoine Quint  &lt;graouts@apple.com&gt;
+
+        [Modern Media Controls] Provide a UI object to show a list of tracks
+        https://bugs.webkit.org/show_bug.cgi?id=165239
+
+        Reviewed by Dean Jackson.
+
+        Adding new tests to cover new TracksPanel functionality.
+
+        * media/modern-media-controls/resources/media-controls-loader.js:
+        * media/modern-media-controls/tracks-panel/tracks-panel-expected.txt: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside-expected.txt: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside.html: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key-expected.txt: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key.html: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-hide-expected.txt: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-hide.html: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-population-expected.txt: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-population.html: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-right-x-expected.txt: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-right-x.html: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard-expected.txt: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard.html: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse-expected.txt: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse.html: Added.
+        * media/modern-media-controls/tracks-panel/tracks-panel.html: Added.
+        * platform/ios-simulator/TestExpectations:
+        * platform/mac/TestExpectations:
+
</ins><span class="cx"> 2016-11-30  Yusuke Suzuki  &lt;utatane.tea@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [JSC] Specifying same module entry point multiple times cause TypeError
</span></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolsresourcesmediacontrolsloaderjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/media/modern-media-controls/resources/media-controls-loader.js (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/resources/media-controls-loader.js        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/LayoutTests/media/modern-media-controls/resources/media-controls-loader.js        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -3,7 +3,7 @@
</span><span class="cx">     const layoutTestsPath = window.location.href.substr(0, window.location.href.indexOf(&quot;/LayoutTests/&quot;));
</span><span class="cx">     const modulePath = layoutTestsPath ? layoutTestsPath + &quot;/Source/WebCore/Modules/modern-media-controls&quot; : &quot;/modern-media-controls&quot;;
</span><span class="cx"> 
</span><del>-    [&quot;media-controls&quot;, &quot;scrubber&quot;, &quot;volume-slider&quot;, &quot;slider&quot;, &quot;button&quot;, &quot;start-button&quot;, &quot;icon-button&quot;, &quot;airplay-button&quot;, &quot;time-label&quot;, &quot;status-label&quot;, &quot;macos-inline-media-controls&quot;, &quot;macos-fullscreen-media-controls&quot;, &quot;ios-inline-media-controls&quot;, &quot;buttons-container&quot;, &quot;placard&quot;].forEach(cssFile =&gt; {
</del><ins>+    [&quot;media-controls&quot;, &quot;scrubber&quot;, &quot;volume-slider&quot;, &quot;slider&quot;, &quot;button&quot;, &quot;start-button&quot;, &quot;icon-button&quot;, &quot;airplay-button&quot;, &quot;time-label&quot;, &quot;status-label&quot;, &quot;macos-inline-media-controls&quot;, &quot;macos-fullscreen-media-controls&quot;, &quot;ios-inline-media-controls&quot;, &quot;buttons-container&quot;, &quot;placard&quot;, &quot;tracks-panel&quot;].forEach(cssFile =&gt; {
</ins><span class="cx">         document.write(`&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;${modulePath}/controls/${cssFile}.css&quot;&gt;`);
</span><span class="cx">     });
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-expected.txt (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-expected.txt        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+Testing the TracksPanel class properties.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+Basic properties
+PASS tracksPanel.element.localName is &quot;div&quot;
+PASS tracksPanel.element.className is &quot;tracks-panel&quot;
+PASS tracksPanel.presented is false
+PASS tracksPanel.rightX is 0
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideclickoutsideexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside-expected.txt (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside-expected.txt        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+Testing a TracksPanel is hidden upon clicking outside of it.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+mediaControls.showTracksPanel()
+
+Clicking outside of the panel
+
+Transition ended
+PASS mediaControls.tracksPanel.presented is false
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideclickoutsidehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside.html (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside.html        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,39 @@
</span><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/media-controls-loader.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;body&gt;
+&lt;script type=&quot;text/javascript&quot;&gt;
+
+window.jsTestIsAsync = true;
+
+description(&quot;Testing a &lt;code&gt;TracksPanel&lt;/code&gt; is hidden upon clicking outside of it.&quot;);
+
+const mediaControls = new MacOSInlineMediaControls({ width: 680, height: 300 });
+document.body.appendChild(mediaControls.element);
+
+debug(&quot;mediaControls.showTracksPanel()&quot;);
+mediaControls.showTracksPanel();
+
+scheduler.frameDidFire = function()
+{
+    window.requestAnimationFrame(() =&gt; {
+        debug(&quot;&quot;);
+        debug(&quot;Clicking outside of the panel&quot;);
+        eventSender.mouseMoveTo(10, 10);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        mediaControls.tracksPanel.element.addEventListener(&quot;transitionend&quot;, (event) =&gt; {
+            debug(&quot;&quot;);
+            debug(&quot;Transition ended&quot;);
+            shouldBeFalse(&quot;mediaControls.tracksPanel.presented&quot;);
+
+            debug(&quot;&quot;);
+            mediaControls.element.remove();
+            finishJSTest();
+        });
+    });
+    scheduler.frameDidFire = null;
+}
+
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideesckeyexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key-expected.txt (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key-expected.txt        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+Testing a TracksPanel is hidden upon hitting the Esc. key.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+mediaControls.showTracksPanel()
+
+Pressing the Escape key
+
+Transition ended
+PASS mediaControls.tracksPanel.presented is false
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideesckeyhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key.html (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key.html        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/media-controls-loader.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;body&gt;
+&lt;script type=&quot;text/javascript&quot;&gt;
+
+window.jsTestIsAsync = true;
+
+description(&quot;Testing a &lt;code&gt;TracksPanel&lt;/code&gt; is hidden upon hitting the Esc. key.&quot;);
+
+const mediaControls = new MacOSInlineMediaControls({ width: 680, height: 300 });
+document.body.appendChild(mediaControls.element);
+
+debug(&quot;mediaControls.showTracksPanel()&quot;);
+mediaControls.showTracksPanel();
+
+scheduler.frameDidFire = function()
+{
+    window.requestAnimationFrame(() =&gt; {
+        debug(&quot;&quot;);
+        debug(&quot;Pressing the Escape key&quot;);
+        eventSender.keyDown(&quot;Escape&quot;);
+        mediaControls.tracksPanel.element.addEventListener(&quot;transitionend&quot;, (event) =&gt; {
+            debug(&quot;&quot;);
+            debug(&quot;Transition ended&quot;);
+            shouldBeFalse(&quot;mediaControls.tracksPanel.presented&quot;);
+
+            debug(&quot;&quot;);
+            mediaControls.element.remove();
+            finishJSTest();
+        });
+    });
+    scheduler.frameDidFire = null;
+}
+
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhideexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-expected.txt (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide-expected.txt        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,18 @@
</span><ins>+Testing a TracksPanel is hidden with an animation.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+tracksPanel.presentInParent(container)
+PASS tracksPanel.presented is true
+
+tracksPanel.hide()
+PASS tracksPanel.presented is true
+
+Transition ended
+PASS tracksPanel.presented is false
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhidehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide.html (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-hide.html        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,40 @@
</span><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/media-controls-loader.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;body&gt;
+&lt;script type=&quot;text/javascript&quot;&gt;
+
+window.jsTestIsAsync = true;
+
+description(&quot;Testing a &lt;code&gt;TracksPanel&lt;/code&gt; is hidden with an animation.&quot;);
+
+const tracksPanel = new TracksPanel;
+const container = new LayoutNode;
+
+document.body.appendChild(container.element);
+
+debug(&quot;tracksPanel.presentInParent(container)&quot;);
+tracksPanel.presentInParent(container);
+
+scheduler.frameDidFire = function()
+{
+    shouldBeTrue(&quot;tracksPanel.presented&quot;);
+
+    debug(&quot;&quot;);
+    debug(&quot;tracksPanel.hide()&quot;);
+    window.requestAnimationFrame(() =&gt; {
+        tracksPanel.hide();
+        shouldBeTrue(&quot;tracksPanel.presented&quot;);
+        tracksPanel.element.addEventListener(&quot;transitionend&quot;, (event) =&gt; {
+            debug(&quot;&quot;);
+            debug(&quot;Transition ended&quot;);
+            shouldBeFalse(&quot;tracksPanel.presented&quot;);
+            debug(&quot;&quot;);
+            finishJSTest();
+        });
+    });
+    scheduler.frameDidFire = null;
+}
+
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelpopulationexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-population-expected.txt (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-population-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-population-expected.txt        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+Populating a TracksPanel instance.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS tracksPanel.presented is true
+PASS tracksPanel.parent is container
+PASS tracksPanel.children.length is numberOfSections
+
+Hierarchy for section #0
+PASS tracksPanel.children[0].element.localName is &quot;div&quot;
+PASS tracksPanel.children[0].element.className is &quot;tracks-panel-section&quot;
+PASS tracksPanel.children[0].children.length is 2
+PASS tracksPanel.children[0].children[0].element.localName is &quot;h3&quot;
+PASS tracksPanel.children[0].children[0].element.textContent is &quot;Title #0&quot;
+PASS tracksPanel.children[0].children[1].element.localName is &quot;ul&quot;
+PASS tracksPanel.children[0].children[1].children.length is 3
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.localName is &quot;li&quot;
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.className is &quot;&quot;
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.textContent is &quot;Track 0x0&quot;
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.localName is &quot;li&quot;
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.className is &quot;selected&quot;
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.textContent is &quot;Track 0x1&quot;
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.localName is &quot;li&quot;
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.className is &quot;&quot;
+PASS tracksPanel.children[0].children[1].children[trackIndex].element.textContent is &quot;Track 0x2&quot;
+
+Hierarchy for section #1
+PASS tracksPanel.children[1].element.localName is &quot;div&quot;
+PASS tracksPanel.children[1].element.className is &quot;tracks-panel-section&quot;
+PASS tracksPanel.children[1].children.length is 2
+PASS tracksPanel.children[1].children[0].element.localName is &quot;h3&quot;
+PASS tracksPanel.children[1].children[0].element.textContent is &quot;Title #1&quot;
+PASS tracksPanel.children[1].children[1].element.localName is &quot;ul&quot;
+PASS tracksPanel.children[1].children[1].children.length is 5
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.localName is &quot;li&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.className is &quot;&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.textContent is &quot;Track 1x0&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.localName is &quot;li&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.className is &quot;&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.textContent is &quot;Track 1x1&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.localName is &quot;li&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.className is &quot;&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.textContent is &quot;Track 1x2&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.localName is &quot;li&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.className is &quot;selected&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.textContent is &quot;Track 1x3&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.localName is &quot;li&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.className is &quot;&quot;
+PASS tracksPanel.children[1].children[1].children[trackIndex].element.textContent is &quot;Track 1x4&quot;
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelpopulationhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-population.html (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-population.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-population.html        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,70 @@
</span><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/media-controls-loader.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;body&gt;
+&lt;script type=&quot;text/javascript&quot;&gt;
+
+description(&quot;Populating a &lt;code&gt;TracksPanel&lt;/code&gt; instance.&quot;);
+
+const tracksPanel = new TracksPanel;
+
+const numberOfSections = 2;
+const numberOfTracksInSection = [3, 5];
+const selectedTracks = [[0, 1], [1, 3]];
+
+tracksPanel.dataSource = {
+    tracksPanelNumberOfSections: function()
+    {
+        return numberOfSections;
+    },
+
+    tracksPanelTitleForSection: function(sectionIndex)
+    {
+        return `Title #${sectionIndex}`;
+    },
+
+    tracksPanelNumberOfTracksInSection: function(sectionIndex)
+    {
+        return numberOfTracksInSection[sectionIndex];
+    },
+
+    tracksPanelTitleForTrackInSection: function(trackIndex, sectionIndex)
+    {
+        return `Track ${sectionIndex}x${trackIndex}`;
+    },
+
+    tracksPanelIsTrackInSectionSelected: function(trackIndex, sectionIndex)
+    {
+        return selectedTracks.some(selectedTrack =&gt; selectedTrack[0] === sectionIndex &amp;&amp; selectedTrack[1] === trackIndex);
+    }
+};
+
+const container = new LayoutNode;
+tracksPanel.presentInParent(container);
+
+shouldBeTrue(&quot;tracksPanel.presented&quot;);
+shouldBe(&quot;tracksPanel.parent&quot;, &quot;container&quot;);
+shouldBe(&quot;tracksPanel.children.length&quot;, &quot;numberOfSections&quot;);
+
+let sectionIndex, trackIndex;
+for (sectionIndex = 0; sectionIndex &lt; numberOfSections; ++sectionIndex) {
+    debug(&quot;&quot;);
+    debug(`Hierarchy for section #${sectionIndex}`);
+    shouldBeEqualToString(`tracksPanel.children[${sectionIndex}].element.localName`, &quot;div&quot;);
+    shouldBeEqualToString(`tracksPanel.children[${sectionIndex}].element.className`, &quot;tracks-panel-section&quot;);
+    shouldBe(`tracksPanel.children[${sectionIndex}].children.length`, &quot;2&quot;);
+    shouldBeEqualToString(`tracksPanel.children[${sectionIndex}].children[0].element.localName`, &quot;h3&quot;);
+    shouldBeEqualToString(`tracksPanel.children[${sectionIndex}].children[0].element.textContent`, tracksPanel.dataSource.tracksPanelTitleForSection(sectionIndex));
+    shouldBeEqualToString(`tracksPanel.children[${sectionIndex}].children[1].element.localName`, &quot;ul&quot;);
+    shouldBe(`tracksPanel.children[${sectionIndex}].children[1].children.length`, `${numberOfTracksInSection[sectionIndex]}`);
+    for (trackIndex = 0; trackIndex &lt; numberOfTracksInSection[sectionIndex]; ++trackIndex) {
+        shouldBeEqualToString(`tracksPanel.children[${sectionIndex}].children[1].children[trackIndex].element.localName`, &quot;li&quot;);
+        shouldBeEqualToString(`tracksPanel.children[${sectionIndex}].children[1].children[trackIndex].element.className`, selectedTracks.some(selectedTrack =&gt; selectedTrack[0] === sectionIndex &amp;&amp; selectedTrack[1] === trackIndex) ? &quot;selected&quot; : &quot;&quot;);
+        shouldBeEqualToString(`tracksPanel.children[${sectionIndex}].children[1].children[trackIndex].element.textContent`, tracksPanel.dataSource.tracksPanelTitleForTrackInSection(trackIndex, sectionIndex));
+    }
+}
+
+debug(&quot;&quot;);
+
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelrightxexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-right-x-expected.txt (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-right-x-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-right-x-expected.txt        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,12 @@
</span><ins>+Testing the TracksPanel class.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+tracksPanel.rightX = 10
+PASS tracksPanel.element.style.right is &quot;10px&quot;
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelrightxhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-right-x.html (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-right-x.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-right-x.html        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/media-controls-loader.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;body&gt;
+&lt;script type=&quot;text/javascript&quot;&gt;
+
+window.jsTestIsAsync = true;
+
+description(&quot;Testing the &lt;code&gt;TracksPanel&lt;/code&gt; class.&quot;);
+
+const tracksPanel = new TracksPanel;
+
+tracksPanel.rightX = 10;
+debug(&quot;tracksPanel.rightX = 10&quot;);
+
+scheduler.frameDidFire = function()
+{
+    shouldBeEqualToString(&quot;tracksPanel.element.style.right&quot;, &quot;10px&quot;);
+
+    debug(&quot;&quot;);
+    finishJSTest();
+};
+
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelselecttrackwithkeyboardexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard-expected.txt (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard-expected.txt        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+Selecting a track in a TracksPanel with the keyboard.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+mediaControls.showTracksPanel()
+
+Focusing the second track by pressing the down arrow key twice
+Obtained focus event
+
+Activating the focused track by pressing the Enter key
+mediaControls.tracksPanel.uiDelegate.tracksPanelSelectionDidChange() called
+sectionIndex = 0
+trackIndex = 1
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelselecttrackwithkeyboardhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard.html (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard.html        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,91 @@
</span><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/media-controls-loader.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;body&gt;
+&lt;style type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
+    
+    .media-controls {
+        position: absolute;
+        top: 0;
+        left: 0;
+    }
+    
+&lt;/style&gt;
+&lt;script type=&quot;text/javascript&quot;&gt;
+
+window.jsTestIsAsync = true;
+
+description(&quot;Selecting a track in a &lt;code&gt;TracksPanel&lt;/code&gt; with the keyboard.&quot;);
+
+const mediaControls = new MacOSInlineMediaControls({ width: 680, height: 300 });
+
+mediaControls.tracksPanel.dataSource = {
+    tracksPanelNumberOfSections: function()
+    {
+        return 1;
+    },
+
+    tracksPanelTitleForSection: function(sectionIndex)
+    {
+        return `Title`;
+    },
+
+    tracksPanelNumberOfTracksInSection: function(sectionIndex)
+    {
+        return 3;
+    },
+
+    tracksPanelTitleForTrackInSection: function(trackIndex, sectionIndex)
+    {
+        return `Track`;
+    },
+
+    tracksPanelIsTrackInSectionSelected: function(trackIndex, sectionIndex)
+    {
+        return false;
+    }
+};
+
+mediaControls.tracksPanel.uiDelegate = {
+    tracksPanelSelectionDidChange: function(trackIndex, sectionIndex)
+    {
+        debug(&quot;mediaControls.tracksPanel.uiDelegate.tracksPanelSelectionDidChange() called&quot;);
+        debug(`sectionIndex = ${sectionIndex}`);
+        debug(`trackIndex = ${trackIndex}`);
+
+        debug(&quot;&quot;);
+        mediaControls.element.remove();
+        finishJSTest();
+    }
+};
+
+document.body.appendChild(mediaControls.element);
+
+let trackElement;
+
+debug(&quot;mediaControls.showTracksPanel()&quot;);
+mediaControls.showTracksPanel();
+
+scheduler.frameDidFire = function()
+{
+    window.requestAnimationFrame(() =&gt; {
+        debug(&quot;&quot;);
+        debug(&quot;Focusing the second track by pressing the down arrow key twice&quot;);
+
+        trackElement = mediaControls.tracksPanel.element.querySelectorAll(&quot;li&quot;)[1];
+        trackElement.addEventListener(&quot;focus&quot;, () =&gt; {
+            debug(&quot;Obtained focus event&quot;);
+
+            debug(&quot;&quot;);
+            debug(&quot;Activating the focused track by pressing the Enter key&quot;);
+            eventSender.keyDown(&quot;Enter&quot;);
+        });
+
+        eventSender.keyDown(&quot;ArrowDown&quot;);
+        eventSender.keyDown(&quot;ArrowDown&quot;);
+    });
+    scheduler.frameDidFire = null;
+}
+
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelselecttrackwithmouseexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse-expected.txt (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse-expected.txt        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+Selecting a track in a TracksPanel with the mouse.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+mediaControls.showTracksPanel()
+
+Mousing over the second track in the panel
+Obtained focus event
+
+Clicking on focused track
+mediaControls.tracksPanel.uiDelegate.tracksPanelSelectionDidChange() called
+sectionIndex = 0
+trackIndex = 1
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelselecttrackwithmousehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse.html (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse.html        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,92 @@
</span><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/media-controls-loader.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;body&gt;
+&lt;style type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
+    
+    .media-controls {
+        position: absolute;
+        top: 0;
+        left: 0;
+    }
+    
+&lt;/style&gt;
+&lt;script type=&quot;text/javascript&quot;&gt;
+
+window.jsTestIsAsync = true;
+
+description(&quot;Selecting a track in a &lt;code&gt;TracksPanel&lt;/code&gt; with the mouse.&quot;);
+
+const mediaControls = new MacOSInlineMediaControls({ width: 680, height: 300 });
+
+mediaControls.tracksPanel.dataSource = {
+    tracksPanelNumberOfSections: function()
+    {
+        return 1;
+    },
+
+    tracksPanelTitleForSection: function(sectionIndex)
+    {
+        return `Title`;
+    },
+
+    tracksPanelNumberOfTracksInSection: function(sectionIndex)
+    {
+        return 3;
+    },
+
+    tracksPanelTitleForTrackInSection: function(trackIndex, sectionIndex)
+    {
+        return `Track`;
+    },
+
+    tracksPanelIsTrackInSectionSelected: function(trackIndex, sectionIndex)
+    {
+        return false;
+    }
+};
+
+mediaControls.tracksPanel.uiDelegate = {
+    tracksPanelSelectionDidChange: function(trackIndex, sectionIndex)
+    {
+        debug(&quot;mediaControls.tracksPanel.uiDelegate.tracksPanelSelectionDidChange() called&quot;);
+        debug(`sectionIndex = ${sectionIndex}`);
+        debug(`trackIndex = ${trackIndex}`);
+
+        debug(&quot;&quot;);
+        mediaControls.element.remove();
+        finishJSTest();
+    }
+};
+
+document.body.appendChild(mediaControls.element);
+
+let trackElement;
+
+debug(&quot;mediaControls.showTracksPanel()&quot;);
+mediaControls.showTracksPanel();
+
+scheduler.frameDidFire = function()
+{
+    window.requestAnimationFrame(() =&gt; {
+        debug(&quot;&quot;);
+        debug(&quot;Mousing over the second track in the panel&quot;);
+
+        trackElement = mediaControls.tracksPanel.element.querySelectorAll(&quot;li&quot;)[1];
+        trackElement.addEventListener(&quot;focus&quot;, () =&gt; {
+            debug(&quot;Obtained focus event&quot;);
+
+            debug(&quot;&quot;);
+            debug(&quot;Clicking on focused track&quot;);
+            eventSender.mouseDown();
+            eventSender.mouseUp();
+        });
+
+        const bounds = trackElement.getBoundingClientRect();
+        eventSender.mouseMoveTo(bounds.left + 1, bounds.top + 1);
+    });
+    scheduler.frameDidFire = null;
+}
+
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolstrackspaneltrackspanelhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel.html (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/tracks-panel/tracks-panel.html        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,20 @@
</span><ins>+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/media-controls-loader.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;body&gt;
+&lt;script type=&quot;text/javascript&quot;&gt;
+
+description(&quot;Testing the &lt;code&gt;TracksPanel&lt;/code&gt; class properties.&quot;);
+
+const tracksPanel = new TracksPanel;
+
+debug(&quot;Basic properties&quot;);
+shouldBeEqualToString(&quot;tracksPanel.element.localName&quot;, &quot;div&quot;);
+shouldBeEqualToString(&quot;tracksPanel.element.className&quot;, &quot;tracks-panel&quot;);
+shouldBeFalse(&quot;tracksPanel.presented&quot;);
+shouldBe(&quot;tracksPanel.rightX&quot;, &quot;0&quot;);
+
+debug(&quot;&quot;);
+
+&lt;/script&gt;
+&lt;script src=&quot;../../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsplatformiossimulatorTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/ios-simulator/TestExpectations (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/ios-simulator/TestExpectations        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/LayoutTests/platform/ios-simulator/TestExpectations        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -2751,6 +2751,9 @@
</span><span class="cx"> media/modern-media-controls/media-controller/media-controller-fullscreen-change.html [ Skip ]
</span><span class="cx"> media/modern-media-controls/media-controller/media-controller-fullscreen-ltr.html  [ Skip ]
</span><span class="cx"> 
</span><ins>+# These tests are all Mac-specific, we never show the tracks menu on iOS
+media/modern-media-controls/tracks-panel
+
</ins><span class="cx"> webkit.org/b/164594 http/tests/cache/disk-cache/disk-cache-request-headers.html [ Pass Timeout ]
</span><span class="cx"> 
</span><span class="cx"> webkit.org/b/163046 js/regress-141098.html [ Pass Failure ]
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/TestExpectations (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/TestExpectations        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/LayoutTests/platform/mac/TestExpectations        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -1452,6 +1452,7 @@
</span><span class="cx"> [ Yosemite ] media/modern-media-controls/placard-support [ Skip ]
</span><span class="cx"> [ Yosemite ] media/modern-media-controls/audio [ Skip ]
</span><span class="cx"> [ Yosemite ] media/modern-media-controls/media-controller/media-controller-fullscreen-ltr.html [ Skip ]
</span><ins>+[ Yosemite ] media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard.html [ Skip ]
</ins><span class="cx"> 
</span><span class="cx"> webkit.org/b/164616 http/tests/media/modern-media-controls/skip-back-support/skip-back-support-button-click.html [ Pass Failure ]
</span><span class="cx"> webkit.org/b/164323 media/modern-media-controls/airplay-support/airplay-support.html [ Pass Failure ]
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/ChangeLog        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -1,3 +1,91 @@
</span><ins>+2016-12-01  Antoine Quint  &lt;graouts@apple.com&gt;
+
+        [Modern Media Controls] Provide a UI object to show a list of tracks
+        https://bugs.webkit.org/show_bug.cgi?id=165239
+
+        Reviewed by Dean Jackson.
+
+        We add a new TracksPanel object which we will be using to display a list of
+        audio and text tracks. The tracks panel can be shown by calling showTracksPanel()
+        on a MacOSMediaControls object and will be dismissed by hitting the Escape key
+        or mousing down outside of the panel's bounds. While the tracks panel is up,
+        arrows can be used to focus individual tracks which can be activated by either
+        pressing the Space bar or Enter key.
+
+        Activating a track will briefly animate its background to indicate selection and
+        dismissing the tracks panel is also animated with a quick fade-out animation.
+
+        Data for the tracks panel is provided by specifying a dataSource property and
+        implementing the required methods to provide the number of sections in the panel,
+        the number of tracks in each section, etc.
+
+        Tests: media/modern-media-controls/tracks-panel/tracks-panel-hide-click-outside.html
+               media/modern-media-controls/tracks-panel/tracks-panel-hide-esc-key.html
+               media/modern-media-controls/tracks-panel/tracks-panel-hide.html
+               media/modern-media-controls/tracks-panel/tracks-panel-population.html
+               media/modern-media-controls/tracks-panel/tracks-panel-right-x.html
+               media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-keyboard.html
+               media/modern-media-controls/tracks-panel/tracks-panel-select-track-with-mouse.html
+               media/modern-media-controls/tracks-panel/tracks-panel.html
+
+        * Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css:
+        (.media-controls.mac.fullscreen):
+        (.media-controls.mac.fullscreen &gt; .controls-bar):
+        (.media-controls.mac.fullscreen .tracks-panel):
+        * Modules/modern-media-controls/controls/macos-inline-media-controls.css:
+        (.media-controls.mac.inline .tracks-panel):
+        * Modules/modern-media-controls/controls/macos-media-controls.js:
+        (MacOSMediaControls.prototype.showTracksPanel):
+        (MacOSMediaControls.prototype.hideTracksPanel):
+        (MacOSMediaControls):
+        * Modules/modern-media-controls/controls/media-controls.css:
+        (.media-controls):
+        * Modules/modern-media-controls/controls/placard.css:
+        (.placard):
+        * Modules/modern-media-controls/controls/status-label.css:
+        (.status-label):
+        * Modules/modern-media-controls/controls/tracks-panel.css: Added.
+        (.tracks-panel):
+        (.tracks-panel *):
+        (.tracks-panel.fade-out):
+        (.tracks-panel-section):
+        (.tracks-panel-section:first-of-type):
+        (.tracks-panel-section &gt; h3):
+        (.tracks-panel-section &gt; ul):
+        (.tracks-panel-section &gt; ul &gt; li):
+        (.tracks-panel-section &gt; ul &gt; li:focus):
+        (.tracks-panel-section &gt; ul &gt; li.selected:before):
+        (.tracks-panel-section &gt; ul &gt; li.animated):
+        (@keyframes tracks-panel-item-selection):
+        (22.22%):
+        * Modules/modern-media-controls/controls/tracks-panel.js: Added.
+        (TracksPanel.prototype.get presented):
+        (TracksPanel.prototype.presentInParent):
+        (TracksPanel.prototype.hide):
+        (TracksPanel.prototype.get rightX):
+        (TracksPanel.prototype.set rightX):
+        (TracksPanel.prototype.trackNodeSelectionAnimationDidEnd):
+        (TracksPanel.prototype.mouseMovedOverTrackNode):
+        (TracksPanel.prototype.mouseExitedTrackNode):
+        (TracksPanel.prototype.commitProperty):
+        (TracksPanel.prototype.handleEvent):
+        (TracksPanel.prototype._childrenFromDataSource.):
+        (TracksPanel.prototype._childrenFromDataSource):
+        (TracksPanel.prototype._handleMousedown):
+        (TracksPanel.prototype._handleKeydown):
+        (TracksPanel.prototype._dismiss):
+        (TracksPanel.prototype._focusTrackNode):
+        (TracksPanel.prototype._focusPreviousTrackNode):
+        (TracksPanel.prototype._focusNextTrackNode):
+        (TracksPanel.prototype._focusFirstTrackNode):
+        (TracksPanel.prototype._focusLastTrackNode):
+        (TrackNode):
+        (TrackNode.prototype.activate):
+        (TrackNode.prototype.handleEvent):
+        (TrackNode.prototype._animationDidEnd):
+        * Modules/modern-media-controls/js-files:
+        * WebCore.xcodeproj/project.pbxproj:
+
</ins><span class="cx"> 2016-12-01  Andreas Kling  &lt;akling@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Log some basic memory usage stats at interesting points in time
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsmacosfullscreenmediacontrolscss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-fullscreen-media-controls.css        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -25,10 +25,15 @@
</span><span class="cx"> 
</span><span class="cx"> /* Controls bar */
</span><span class="cx"> 
</span><ins>+.media-controls.mac.fullscreen {
+    --controls-bar-width: 468px;
+    --tracks-panel-right-margin: 40px;
+}
+
</ins><span class="cx"> .media-controls.mac.fullscreen &gt; .controls-bar {
</span><span class="cx">     left: 50%;
</span><span class="cx">     bottom: 25px;
</span><del>-    width: 468px;
</del><ins>+    width: var(--controls-bar-width);
</ins><span class="cx">     height: 75px;
</span><span class="cx">     transform: translateX(-50%);
</span><span class="cx">     overflow: hidden;
</span><span class="lines">@@ -125,3 +130,11 @@
</span><span class="cx"> .media-controls.mac.fullscreen .scrubber {
</span><span class="cx">     top: 7px;
</span><span class="cx"> }
</span><ins>+
+/* Tracks Panel */
+
+.media-controls.mac.fullscreen .tracks-panel {
+    /* Half of the screen width minus half of the controls bar width minus the distance to the right edge of the tracks button */
+    right: calc(50% - var(--controls-bar-width) / 2 + var(--tracks-panel-right-margin));
+    bottom: 234px;
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsmacosinlinemediacontrolscss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-inline-media-controls.css        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -126,3 +126,9 @@
</span><span class="cx">     left: -15px;
</span><span class="cx">     top: 40px;
</span><span class="cx"> }
</span><ins>+
+/* Tracks Panel */
+
+.media-controls.mac.inline .tracks-panel {
+    bottom: 51px;
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsmacosmediacontrolsjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/macos-media-controls.js        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -34,7 +34,26 @@
</span><span class="cx"> 
</span><span class="cx">         this.muteButton = new MuteButton(this);
</span><span class="cx">         this.tracksButton = new TracksButton(this);
</span><ins>+        this.tracksPanel = new TracksPanel;
</ins><span class="cx">         this.volumeSlider = new VolumeSlider;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    // Public
+
+    showTracksPanel()
+    {
+        this.tracksButton.on = true;
+        this.tracksButton.element.blur();
+        this.controlsBar.userInteractionEnabled = false;
+        this.tracksPanel.presentInParent(this);
+    }
+
+    hideTracksPanel()
+    {
+        this.tracksButton.on = false;
+        this.tracksButton.element.focus();
+        this.controlsBar.userInteractionEnabled = true;
+        this.tracksPanel.hide();
+    }
+
</ins><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsmediacontrolscss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/media-controls.css        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> /* We need to use relative positioning due to webkit.org/b/163603 */
</span><span class="cx"> .media-controls {
</span><span class="cx">     position: relative;
</span><ins>+    font-family: -apple-system;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .media-controls &gt; .controls-bar {
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsplacardcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/placard.css (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/placard.css        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/placard.css        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -32,8 +32,6 @@
</span><span class="cx"> 
</span><span class="cx">     background-color: black;
</span><span class="cx">     color: rgb(164, 164, 164);
</span><del>-    
-    font-family: -apple-system;
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .placard .container {
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsstatuslabelcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/status-label.css (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/status-label.css        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/status-label.css        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -30,7 +30,6 @@
</span><span class="cx">     white-space: nowrap;
</span><span class="cx">     overflow: hidden;
</span><span class="cx"> 
</span><del>-    font-family: -apple-system;
</del><span class="cx">     font-size: 14px;
</span><span class="cx"> 
</span><span class="cx">     color: rgba(255, 255, 255, 0.572);
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolstrackspanelcss"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css                                (rev 0)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.css        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,104 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.tracks-panel {
+    position: absolute;
+    display: inline-block;
+    border-radius: 7px;
+    /* FIXME: we want to use the real System Dark treatment here, see &lt;rdar://problem/19993961&gt; */
+    background-color: rgba(30, 30, 30, 0.45);
+    -webkit-backdrop-filter: saturate(180%) blur(20px);
+}
+
+.tracks-panel * {
+    font-size: 14px;
+    font-weight: 500;
+}
+
+.tracks-panel.fade-out {
+    transition-property: opacity;
+    transition-duration: 265ms;
+    opacity: 0;
+}
+
+.tracks-panel-section {
+    border-top: 2px solid rgb(104, 104, 104);
+}
+
+.tracks-panel-section:first-of-type {
+    border-top: 0;
+}
+
+.tracks-panel-section &gt; h3 {
+    color: rgb(150, 150, 150);
+    padding: 5px 20px 1px 21px;
+    margin: 0;
+}
+
+.tracks-panel-section &gt; ul {
+    list-style-type: none;
+    margin-top: 0;
+    padding: 0;
+}
+
+.tracks-panel-section &gt; ul &gt; li {
+    position: relative;
+    padding: 1px 20px 1px 33px;
+    color: white;
+}
+
+.tracks-panel-section &gt; ul &gt; li:focus {
+    background-color: rgba(26, 68, 243, 0.6);
+    -webkit-backdrop-filter: saturate(180%) blur(20px);
+    outline: none;
+}
+
+.tracks-panel-section &gt; ul &gt; li.selected:before {
+    position: absolute;
+    top: 3px;
+    left: 12px;
+    width: 12px;
+    display: inline-block;
+    content: url('data:image/svg+xml,&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 300 300&quot;&gt;&lt;polygon fill=&quot;white&quot; points=&quot;252.301,4.477 126.667,194.104 43.358,108.3 6.868,161.408 132.515,290.814 297.732,49.926&quot;/&gt;&lt;/svg&gt;');
+}
+
+.tracks-panel-section &gt; ul &gt; li.animated {
+    animation-name: tracks-panel-item-selection;
+    animation-duration: 150ms;
+    animation-timing-function: step-end;
+    animation-fill-mode: forwards;
+}
+
+@keyframes tracks-panel-item-selection {
+    0%, 55.55% {
+        background-color: rgba(26, 68, 243, 0.6);
+        -webkit-backdrop-filter: saturate(180%) blur(20px);
+    }
+
+    22.22% {
+        background: none;
+        -webkit-backdrop-filter: none;
+    }
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolstrackspaneljs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js (0 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js                                (rev 0)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -0,0 +1,277 @@
</span><ins>+
+class TracksPanel extends LayoutNode
+{
+
+    constructor()
+    {
+        super(`&lt;div class=&quot;tracks-panel&quot;&gt;`);
+        this._rightX = 0;
+    }
+
+    // Public
+
+    get presented()
+    {
+        return !!this.parent;
+    }
+
+    presentInParent(node)
+    {
+        if (this.parent === node)
+            return;
+
+        this.children = this._childrenFromDataSource();
+
+        node.addChild(this);
+
+        this.element.removeEventListener(&quot;transitionend&quot;, this);
+        this.element.classList.remove(&quot;fade-out&quot;);
+
+        window.addEventListener(&quot;mousedown&quot;, this, true);
+        window.addEventListener(&quot;keydown&quot;, this, true);
+
+        this._focusedTrackNode = null;
+    }
+
+    hide()
+    {
+        if (!this.presented)
+            return;
+
+        window.removeEventListener(&quot;mousedown&quot;, this, true);
+        window.removeEventListener(&quot;keydown&quot;, this, true);
+
+        this.element.addEventListener(&quot;transitionend&quot;, this);
+        this.element.classList.add(&quot;fade-out&quot;);
+    }
+
+    get rightX()
+    {
+        return this._rightX;
+    }
+
+    set rightX(x)
+    {
+        if (this._rightX === x)
+            return;
+
+        this._rightX = x;
+        this.markDirtyProperty(&quot;rightX&quot;);
+    }
+
+    // Protected
+
+    trackNodeSelectionAnimationDidEnd(trackNode)
+    {
+        if (this.uiDelegate &amp;&amp; typeof this.uiDelegate.tracksPanelSelectionDidChange === &quot;function&quot;)
+            this.uiDelegate.tracksPanelSelectionDidChange(trackNode.index, trackNode.sectionIndex);
+    }
+
+    mouseMovedOverTrackNode(trackNode)
+    {
+        this._focusTrackNode(trackNode);
+    }
+
+    mouseExitedTrackNode(trackNode)
+    {
+        this._focusedTrackNode.element.blur();
+        delete this._focusedTrackNode;
+    }
+
+    commitProperty(propertyName)
+    {
+        if (propertyName === &quot;rightX&quot;)
+            this.element.style.right = `${this._rightX}px`;
+        else
+            super.commitProperty(propertyName);
+    }
+
+    handleEvent(event)
+    {
+        switch (event.type) {
+        case &quot;mousedown&quot;:
+            this._handleMousedown(event);
+            break;
+        case &quot;keydown&quot;:
+            this._handleKeydown(event);
+            break;
+        case &quot;transitionend&quot;:
+            this.remove();
+            break;
+        }
+    }
+
+    // Private
+
+    _childrenFromDataSource()
+    {
+        const children = [];
+
+        this._trackNodes = [];
+        
+        const dataSource = this.dataSource;
+        if (!dataSource)
+            return children;
+        
+        const numberOfSections = dataSource.tracksPanelNumberOfSections();
+        if (numberOfSections == 0)
+            return children;
+
+        for (let sectionIndex = 0; sectionIndex &lt; numberOfSections; ++sectionIndex) {
+            let sectionNode = new LayoutNode(`&lt;div class=&quot;tracks-panel-section&quot;&gt;&lt;/div&gt;`);
+            sectionNode.addChild(new LayoutNode(`&lt;h3&gt;${dataSource.tracksPanelTitleForSection(sectionIndex)}&lt;/h3&gt;`));
+
+            let tracksListNode = sectionNode.addChild(new LayoutNode(`&lt;ul&gt;&lt;/ul&gt;`));
+            let numberOfTracks = dataSource.tracksPanelNumberOfTracksInSection(sectionIndex);
+            for (let trackIndex = 0; trackIndex &lt; numberOfTracks; ++trackIndex) {
+                let trackTitle = dataSource.tracksPanelTitleForTrackInSection(trackIndex, sectionIndex);
+                let trackSelected = dataSource.tracksPanelIsTrackInSectionSelected(trackIndex, sectionIndex)
+                let trackNode = tracksListNode.addChild(new TrackNode(trackIndex, sectionIndex, trackTitle, trackSelected, this));
+                this._trackNodes.push(trackNode);
+            }
+            children.push(sectionNode);
+        }
+        
+        return children;
+    }
+
+    _handleMousedown(event)
+    {
+        if (this.element.contains(event.target))
+            return;
+
+        this._dismiss();
+
+        event.preventDefault();
+        event.stopPropagation();
+    }
+
+    _handleKeydown(event)
+    {
+        switch (event.key) {
+        case &quot;Home&quot;:
+        case &quot;PageUp&quot;:
+            this._focusFirstTrackNode();
+            break;
+        case &quot;End&quot;:
+        case &quot;PageDown&quot;:
+            this._focusLastTrackNode();
+            break;
+        case &quot;ArrowDown&quot;:
+            if (event.altKey || event.metaKey)
+                this._focusLastTrackNode();
+            else
+                this._focusNextTrackNode();
+            break;
+        case &quot;ArrowUp&quot;:
+            if (event.altKey || event.metaKey)
+                this._focusFirstTrackNode();
+            else
+                this._focusPreviousTrackNode();
+            break;
+        case &quot; &quot;:
+        case &quot;Enter&quot;:
+            if (this._focusedTrackNode)
+                this._focusedTrackNode.activate();
+            break;
+        case &quot;Escape&quot;:
+            this._dismiss();
+            break;
+        }
+    }
+
+    _dismiss()
+    {
+        if (this.parent &amp;&amp; typeof this.parent.hideTracksPanel === &quot;function&quot;)
+            this.parent.hideTracksPanel();
+    }
+
+    _focusTrackNode(trackNode)
+    {
+        if (!trackNode || trackNode === this._focusedTrackNode)
+            return;
+
+        trackNode.element.focus();
+        this._focusedTrackNode = trackNode;
+    }
+
+    _focusPreviousTrackNode()
+    {
+        const previousIndex = this._focusedTrackNode ? this._trackNodes.indexOf(this._focusedTrackNode) - 1 : this._trackNodes.length - 1;
+        this._focusTrackNode(this._trackNodes[previousIndex]);
+    }
+
+    _focusNextTrackNode()
+    {
+        this._focusTrackNode(this._trackNodes[this._trackNodes.indexOf(this._focusedTrackNode) + 1]);
+    }
+
+    _focusFirstTrackNode()
+    {
+        this._focusTrackNode(this._trackNodes[0]);
+    }
+
+    _focusLastTrackNode()
+    {
+        this._focusTrackNode(this._trackNodes[this._trackNodes.length - 1]);
+    }
+
+}
+
+class TrackNode extends LayoutNode
+{
+
+    constructor(index, sectionIndex, title, selected, panel)
+    {
+        super(`&lt;li tabindex=&quot;0&quot;&gt;${title}&lt;/li&gt;`);
+
+        this.index = index;
+        this.sectionIndex = sectionIndex;
+        this._panel = panel;
+        this._selected = selected;
+
+        if (selected)
+            this.element.classList.add(&quot;selected&quot;);
+
+        this.element.addEventListener(&quot;mousemove&quot;, this);
+        this.element.addEventListener(&quot;mouseleave&quot;, this);
+        this.element.addEventListener(&quot;click&quot;, this);
+    }
+
+    // Public
+
+    activate()
+    {
+        this.element.addEventListener(&quot;animationend&quot;, this);
+        this.element.classList.add(&quot;animated&quot;);
+    }
+
+    // Protected
+
+    handleEvent(event)
+    {
+        switch (event.type) {
+        case &quot;mousemove&quot;:
+            this._panel.mouseMovedOverTrackNode(this);
+            break;
+        case &quot;mouseleave&quot;:
+            this._panel.mouseExitedTrackNode(this);
+            break;
+        case &quot;click&quot;:
+            this.activate();
+            break;
+        case &quot;animationend&quot;:
+            this._animationDidEnd();
+            break;
+        }
+    }
+
+    // Private
+
+    _animationDidEnd()
+    {
+        this.element.removeEventListener(&quot;animationend&quot;, this);
+        this._panel.trackNodeSelectionAnimationDidEnd(this);
+    }
+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolsjsfiles"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/js-files (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/js-files        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/Modules/modern-media-controls/js-files        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -23,6 +23,7 @@
</span><span class="cx"> controls/buttons-container.js
</span><span class="cx"> controls/status-label.js
</span><span class="cx"> controls/controls-bar.js
</span><ins>+controls/tracks-panel.js
</ins><span class="cx"> controls/media-controls.js
</span><span class="cx"> controls/ios-inline-media-controls.js
</span><span class="cx"> controls/macos-media-controls.js
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (209181 => 209182)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2016-12-01 16:15:27 UTC (rev 209181)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2016-12-01 16:16:38 UTC (rev 209182)
</span><span class="lines">@@ -10038,6 +10038,8 @@
</span><span class="cx">                 713E70AF1733E8B300A22D00 /* plugIns.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = plugIns.js; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 714131471DC9D6AF00336107 /* fullscreen-support.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = &quot;fullscreen-support.js&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 714131481DC9D6EF00336107 /* js-files */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = &quot;js-files&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                7146DF8B1DEFC2ED0046F98B /* tracks-panel.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = &quot;tracks-panel.css&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
+                7146DF8C1DEFC2ED0046F98B /* tracks-panel.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = &quot;tracks-panel.js&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 714F5B051DEE5F740075BD17 /* invalid-placard.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = &quot;invalid-placard.js&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 714F5B061DEE5F8A0075BD17 /* invalid-placard@1x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = &quot;invalid-placard@1x.png&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 714F5B071DEE5F8A0075BD17 /* invalid-placard@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = &quot;invalid-placard@2x.png&quot;; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -18065,6 +18067,8 @@
</span><span class="cx">                 716FA0D71DB26591007323CC /* controls */ = {
</span><span class="cx">                         isa = PBXGroup;
</span><span class="cx">                         children = (
</span><ins>+                                7146DF8B1DEFC2ED0046F98B /* tracks-panel.css */,
+                                7146DF8C1DEFC2ED0046F98B /* tracks-panel.js */,
</ins><span class="cx">                                 716FA0D81DB26591007323CC /* airplay-button.css */,
</span><span class="cx">                                 716FA0D91DB26591007323CC /* airplay-button.js */,
</span><span class="cx">                                 716FA0DA1DB26591007323CC /* airplay-placard.js */,
</span></span></pre>
</div>
</div>

</body>
</html>