<!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>[207111] 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/207111">207111</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-10-11 05:52:28 -0700 (Tue, 11 Oct 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Modern Media Controls] Buttons container
https://bugs.webkit.org/show_bug.cgi?id=163238
&lt;rdar://problem/28701864&gt;

Patch by Antoine Quint &lt;graouts@apple.com&gt; on 2016-10-11
Reviewed by Dean Jackson.

Source/WebCore:

We add a new ButtonsContainer class which contains a group of Button objects
and positions them based on the provided padding and margin between buttons.
Buttons that aren't enabled or marked as dropped are not added to the tree of
LayoutNodes, and thus the DOM.

Additionally, we fix a few issues we found while working on tests for ButtonsContainer
where LayoutNodes would schedule layout callbacks even when they would not do any work
during the layout callback due to not resetting the `needsLayout` flag to false and
removing any scheduled tasks when a layout was completed.

Finally, we fix a few style issues that had not been caught so far and an unused
`size` property on IconButton.

Tests: media/modern-media-controls/buttons-container/buttons-container-buttons-property.html
       media/modern-media-controls/buttons-container/buttons-container-constructor.html
       media/modern-media-controls/buttons-container/buttons-container-layout.html

* Modules/modern-media-controls/controls/airplay-button.js:
(AirplayButton.prototype.set on):
(AirplayButton):
* Modules/modern-media-controls/controls/buttons-container.css:
(.buttons-container):
* Modules/modern-media-controls/controls/buttons-container.js:
(ButtonsContainer.prototype.get buttons):
(ButtonsContainer.prototype.set buttons):
(ButtonsContainer.prototype.layout):
* Modules/modern-media-controls/controls/icon-button.js:
* Modules/modern-media-controls/controls/layout-node.js:
(LayoutNode.prototype.set needsLayout):
(LayoutNode.prototype.markDirtyProperty):
(LayoutNode.prototype._markNodeManipulation):
(LayoutNode.prototype._updateDirtyState):
(performScheduledLayout):
(elementFromString):
* Modules/modern-media-controls/controls/scheduler.js:
(const.scheduler.new.prototype.unscheduleLayout):

LayoutTests:

Adding tests for the new ButtonsContainer class.

* media/modern-media-controls/buttons-container/buttons-container-buttons-property-expected.txt: Added.
* media/modern-media-controls/buttons-container/buttons-container-buttons-property.html: Added.
* media/modern-media-controls/buttons-container/buttons-container-constructor-expected.txt: Added.
* media/modern-media-controls/buttons-container/buttons-container-constructor.html: Added.
* media/modern-media-controls/buttons-container/buttons-container-layout-expected.txt: Added.
* media/modern-media-controls/buttons-container/buttons-container-layout.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsiconbuttonjs">trunk/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolslayoutnodejs">trunk/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsschedulerjs">trunk/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/LayoutTests/media/modern-media-controls/buttons-container/</li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerbuttonspropertyexpectedtxt">trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-buttons-property-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerbuttonspropertyhtml">trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-buttons-property.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerconstructorexpectedtxt">trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-constructor-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerconstructorhtml">trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-constructor.html</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerlayoutexpectedtxt">trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-layout-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerlayouthtml">trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-layout.html</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsbuttonscontainercss">trunk/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css</a></li>
<li><a href="#trunkSourceWebCoreModulesmodernmediacontrolscontrolsbuttonscontainerjs">trunk/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (207110 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-10-11 12:47:58 UTC (rev 207110)
+++ trunk/LayoutTests/ChangeLog        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2016-10-11  Antoine Quint  &lt;graouts@apple.com&gt;
+
+        [Modern Media Controls] Buttons container
+        https://bugs.webkit.org/show_bug.cgi?id=163238
+        &lt;rdar://problem/28701864&gt;
+
+        Reviewed by Dean Jackson.
+
+        Adding tests for the new ButtonsContainer class.
+
+        * media/modern-media-controls/buttons-container/buttons-container-buttons-property-expected.txt: Added.
+        * media/modern-media-controls/buttons-container/buttons-container-buttons-property.html: Added.
+        * media/modern-media-controls/buttons-container/buttons-container-constructor-expected.txt: Added.
+        * media/modern-media-controls/buttons-container/buttons-container-constructor.html: Added.
+        * media/modern-media-controls/buttons-container/buttons-container-layout-expected.txt: Added.
+        * media/modern-media-controls/buttons-container/buttons-container-layout.html: Added.
+
</ins><span class="cx"> 2016-10-11  Youenn Fablet  &lt;youenn@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Fetch API] Support Request cache mode
</span></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerbuttonspropertyexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-buttons-property-expected.txt (0 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-buttons-property-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-buttons-property-expected.txt        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -0,0 +1,12 @@
</span><ins>+Testing ButtonsContainer buttons property.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS container.buttons.length is 3
+PASS container.children.length is 3
+PASS container.buttons is container.children
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerbuttonspropertyhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-buttons-property.html (0 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-buttons-property.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-buttons-property.html        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+&lt;link rel=&quot;stylesheet&quot; href=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/button.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
+&lt;link rel=&quot;stylesheet&quot; href=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/scheduler.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/layout-node.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/layout-item.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/button.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/buttons-container.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 &lt;code&gt;ButtonsContainer&lt;/code&gt; buttons property.&quot;);
+
+window.jsTestIsAsync = true;
+
+const container = new ButtonsContainer({
+    margin: 10,
+    padding: 20
+});
+
+container.buttons = [new Button, new Button, new Button];
+
+scheduler.frameDidFire = function()
+{
+    shouldBe(&quot;container.buttons.length&quot;, &quot;3&quot;);
+    shouldBe(&quot;container.children.length&quot;, &quot;3&quot;);
+    shouldBe(&quot;container.buttons&quot;, &quot;container.children&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="trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerconstructorexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-constructor-expected.txt (0 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-constructor-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-constructor-expected.txt        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -0,0 +1,20 @@
</span><ins>+Testing the ButtonsContainer constructor.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS defaultContainer.element.localName is &quot;div&quot;
+PASS defaultContainer.element.className.trim() is &quot;buttons-container&quot;
+PASS defaultContainer.margin is 0
+PASS defaultContainer.padding is 0
+PASS defaultContainer.buttons is []
+PASS containerWithParameters.element.localName is &quot;div&quot;
+PASS containerWithParameters.element.classList.contains('buttons-container') is true
+PASS containerWithParameters.element.classList.contains('foo') is true
+PASS containerWithParameters.margin is 10
+PASS containerWithParameters.padding is 20
+PASS containerWithParameters.buttons is buttons
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerconstructorhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-constructor.html (0 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-constructor.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-constructor.html        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+&lt;link rel=&quot;stylesheet&quot; href=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/button.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
+&lt;link rel=&quot;stylesheet&quot; href=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/scheduler.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/layout-node.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/layout-item.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/button.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/buttons-container.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;ButtonsContainer&lt;/code&gt; constructor.&quot;);
+
+const defaultContainer = new ButtonsContainer;
+shouldBeEqualToString(&quot;defaultContainer.element.localName&quot;, &quot;div&quot;);
+shouldBeEqualToString(&quot;defaultContainer.element.className.trim()&quot;, &quot;buttons-container&quot;);
+shouldBe(&quot;defaultContainer.margin&quot;, &quot;0&quot;);
+shouldBe(&quot;defaultContainer.padding&quot;, &quot;0&quot;);
+shouldBe(&quot;defaultContainer.buttons&quot;, &quot;[]&quot;);
+
+const buttons = [new Button, new Button];
+const containerWithParameters = new ButtonsContainer({
+    margin: 10,
+    padding: 20,
+    buttons: buttons,
+    cssClassName: &quot;foo&quot;
+});
+shouldBeEqualToString(&quot;containerWithParameters.element.localName&quot;, &quot;div&quot;);
+shouldBeTrue(&quot;containerWithParameters.element.classList.contains('buttons-container')&quot;);
+shouldBeTrue(&quot;containerWithParameters.element.classList.contains('foo')&quot;);
+shouldBe(&quot;containerWithParameters.margin&quot;, &quot;10&quot;);
+shouldBe(&quot;containerWithParameters.padding&quot;, &quot;20&quot;);
+shouldBe(&quot;containerWithParameters.buttons&quot;, &quot;buttons&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="trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerlayoutexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-layout-expected.txt (0 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-layout-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-layout-expected.txt        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -0,0 +1,17 @@
</span><ins>+Testing ButtonsContainer layout.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS container.children.length is 3
+PASS container.children[0] is tenPtWideButton
+PASS container.children[1] is twentyPtWideButton
+PASS container.children[2] is thirtyPtWideButton
+PASS tenPtWideButton.x is 20
+PASS twentyPtWideButton.x is 40
+PASS thirtyPtWideButton.x is 70
+PASS container.width is 120
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamodernmediacontrolsbuttonscontainerbuttonscontainerlayouthtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-layout.html (0 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-layout.html                                (rev 0)
+++ trunk/LayoutTests/media/modern-media-controls/buttons-container/buttons-container-layout.html        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -0,0 +1,51 @@
</span><ins>+&lt;link rel=&quot;stylesheet&quot; href=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/button.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
+&lt;link rel=&quot;stylesheet&quot; href=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
+&lt;script src=&quot;../../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/scheduler.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/layout-node.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/layout-item.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/button.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../../../Source/WebCore/Modules/modern-media-controls/controls/buttons-container.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 &lt;code&gt;ButtonsContainer&lt;/code&gt; layout.&quot;);
+
+const tenPtWideButton = new Button;
+tenPtWideButton.width = 10;
+
+const twentyPtWideButton = new Button;
+twentyPtWideButton.width = 20;
+
+const thirtyPtWideButton = new Button;
+thirtyPtWideButton.width = 30;
+
+// Should be disregarded by the container.
+const disabledButton = new Button;
+disabledButton.width = 15;
+disabledButton.enabled = false;
+
+// Should be disregarded by the container.
+const droppedButton = new Button;
+droppedButton.width = 25;
+droppedButton.dropped = true;
+
+const container = new ButtonsContainer({
+    margin: 10,
+    padding: 20,
+    buttons: [tenPtWideButton, disabledButton, twentyPtWideButton, droppedButton, thirtyPtWideButton]
+});
+
+container.layout();
+shouldBe(&quot;container.children.length&quot;, &quot;3&quot;);
+shouldBe(&quot;container.children[0]&quot;, &quot;tenPtWideButton&quot;);
+shouldBe(&quot;container.children[1]&quot;, &quot;twentyPtWideButton&quot;);
+shouldBe(&quot;container.children[2]&quot;, &quot;thirtyPtWideButton&quot;);
+shouldBe(&quot;tenPtWideButton.x&quot;, &quot;20&quot;);
+shouldBe(&quot;twentyPtWideButton.x&quot;, &quot;40&quot;);
+shouldBe(&quot;thirtyPtWideButton.x&quot;, &quot;70&quot;);
+shouldBe(&quot;container.width&quot;, &quot;120&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="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (207110 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-10-11 12:47:58 UTC (rev 207110)
+++ trunk/Source/WebCore/ChangeLog        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -1,3 +1,48 @@
</span><ins>+2016-10-11  Antoine Quint  &lt;graouts@apple.com&gt;
+
+        [Modern Media Controls] Buttons container
+        https://bugs.webkit.org/show_bug.cgi?id=163238
+        &lt;rdar://problem/28701864&gt;
+
+        Reviewed by Dean Jackson.
+
+        We add a new ButtonsContainer class which contains a group of Button objects
+        and positions them based on the provided padding and margin between buttons.
+        Buttons that aren't enabled or marked as dropped are not added to the tree of
+        LayoutNodes, and thus the DOM.
+
+        Additionally, we fix a few issues we found while working on tests for ButtonsContainer
+        where LayoutNodes would schedule layout callbacks even when they would not do any work
+        during the layout callback due to not resetting the `needsLayout` flag to false and
+        removing any scheduled tasks when a layout was completed.
+
+        Finally, we fix a few style issues that had not been caught so far and an unused
+        `size` property on IconButton.
+
+        Tests: media/modern-media-controls/buttons-container/buttons-container-buttons-property.html
+               media/modern-media-controls/buttons-container/buttons-container-constructor.html
+               media/modern-media-controls/buttons-container/buttons-container-layout.html
+
+        * Modules/modern-media-controls/controls/airplay-button.js:
+        (AirplayButton.prototype.set on):
+        (AirplayButton):
+        * Modules/modern-media-controls/controls/buttons-container.css:
+        (.buttons-container):
+        * Modules/modern-media-controls/controls/buttons-container.js:
+        (ButtonsContainer.prototype.get buttons):
+        (ButtonsContainer.prototype.set buttons):
+        (ButtonsContainer.prototype.layout):
+        * Modules/modern-media-controls/controls/icon-button.js:
+        * Modules/modern-media-controls/controls/layout-node.js:
+        (LayoutNode.prototype.set needsLayout):
+        (LayoutNode.prototype.markDirtyProperty):
+        (LayoutNode.prototype._markNodeManipulation):
+        (LayoutNode.prototype._updateDirtyState):
+        (performScheduledLayout):
+        (elementFromString):
+        * Modules/modern-media-controls/controls/scheduler.js:
+        (const.scheduler.new.prototype.unscheduleLayout):
+
</ins><span class="cx"> 2016-10-11  Youenn Fablet  &lt;youenn@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Fetch API] Support Request cache mode
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsbuttonscontainercss"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css (0 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css                                (rev 0)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.css        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -0,0 +1,29 @@
</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.
+ */
+
+.buttons-container {
+    position: absolute;
+    height: 100%;
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsbuttonscontainerjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js (0 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js                                (rev 0)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/buttons-container.js        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -0,0 +1,77 @@
</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.
+ */
+
+class ButtonsContainer extends LayoutNode
+{
+
+    constructor({ buttons = [], padding = 0, margin = 0, cssClassName = &quot;&quot; } = {})
+    {
+        super(`&lt;div class=&quot;buttons-container ${cssClassName}&quot;&gt;`);
+
+        this.margin = margin;
+        this.padding = padding;
+        this.buttons = buttons;
+    }
+
+    // Public
+
+    get buttons()
+    {
+        return this._buttons;
+    }
+
+    set buttons(buttons)
+    {
+        if (!Array.isArray(buttons))
+            return;
+
+        this._buttons = buttons;
+        this.needsLayout = true;
+    }
+
+    layout()
+    {
+        super.layout();
+
+        const children = [];
+        let x = this.padding;
+
+        this._buttons.forEach(button =&gt; {
+            if (!button.enabled || button.dropped)
+                return;
+            button.x = x;
+            x += button.width + this.margin;
+            children.push(button);
+        });
+
+        if (children.length)
+            this.width = x - this.margin + this.padding;
+        else
+            this.width = this.padding * 2;
+
+        this.children = children;
+    }
+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsiconbuttonjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js (207110 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js        2016-10-11 12:47:58 UTC (rev 207110)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/icon-button.js        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -39,8 +39,6 @@
</span><span class="cx"> 
</span><span class="cx">         if (!!iconName)
</span><span class="cx">             this.iconName = iconName;
</span><del>-
-        this.size = { width: 0, height: 0 };
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Public
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolslayoutnodejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js (207110 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js        2016-10-11 12:47:58 UTC (rev 207110)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/layout-node.js        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -95,7 +95,7 @@
</span><span class="cx">         if (this.needsLayout === flag)
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        this._needsLayout = true;
</del><ins>+        this._needsLayout = flag;
</ins><span class="cx">         this._updateDirtyState();
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -167,7 +167,8 @@
</span><span class="cx">             return this._parent.removeChild(this);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    markDirtyProperty(propertyName) {
</del><ins>+    markDirtyProperty(propertyName)
+    {
</ins><span class="cx">         const hadProperty = this._dirtyProperties.has(propertyName);
</span><span class="cx">         this._dirtyProperties.add(propertyName);
</span><span class="cx"> 
</span><span class="lines">@@ -217,17 +218,22 @@
</span><span class="cx"> 
</span><span class="cx">     // Private
</span><span class="cx"> 
</span><del>-    _markNodeManipulation(manipulation) {
</del><ins>+    _markNodeManipulation(manipulation)
+    {
</ins><span class="cx">         this._pendingDOMManipulation = manipulation;
</span><span class="cx">         this._updateDirtyState();
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    _updateDirtyState() {
</del><ins>+    _updateDirtyState()
+    {
</ins><span class="cx">         if (this.needsLayout) {
</span><span class="cx">             dirtyNodes.add(this);
</span><span class="cx">             scheduler.scheduleLayout(performScheduledLayout);
</span><del>-        } else
-            dirtyNodes.delete(node);
</del><ins>+        } else {
+            dirtyNodes.delete(this);
+            if (dirtyNodes.size === 0)
+                scheduler.unscheduleLayout(performScheduledLayout);
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _updateChildren()
</span><span class="lines">@@ -255,15 +261,21 @@
</span><span class="cx">     Addition: 2
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-function performScheduledLayout() {
-    dirtyNodes.forEach(node =&gt; node.layout());
</del><ins>+function performScheduledLayout()
+{
+    dirtyNodes.forEach(node =&gt; {
+        node.needsLayout = false;
+        node.layout()
+    });
</ins><span class="cx">     dirtyNodes.clear();
</span><ins>+    scheduler.unscheduleLayout(performScheduledLayout);
</ins><span class="cx"> 
</span><span class="cx">     nodesRequiringChildrenUpdate.forEach(node =&gt; node._updateChildren());
</span><span class="cx">     nodesRequiringChildrenUpdate.clear();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-function elementFromString(elementString) {
</del><ins>+function elementFromString(elementString)
+{
</ins><span class="cx">     const element = document.createElement(&quot;div&quot;);
</span><span class="cx">     element.innerHTML = elementString;
</span><span class="cx">     return element.firstElementChild;
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesmodernmediacontrolscontrolsschedulerjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js (207110 => 207111)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js        2016-10-11 12:47:58 UTC (rev 207110)
+++ trunk/Source/WebCore/Modules/modern-media-controls/controls/scheduler.js        2016-10-11 12:52:28 UTC (rev 207111)
</span><span class="lines">@@ -19,6 +19,14 @@
</span><span class="cx">         this._requestFrameIfNeeded();
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    unscheduleLayout(callback)
+    {
+        if (typeof callback !== &quot;function&quot;)
+            return;
+
+        this._layoutCallbacks.delete(callback);
+    }
+
</ins><span class="cx">     // Private
</span><span class="cx"> 
</span><span class="cx">     _requestFrameIfNeeded()
</span></span></pre>
</div>
</div>

</body>
</html>