<!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>[210938] trunk/Websites/perf.webkit.org</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/210938">210938</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2017-01-19 14:58:46 -0800 (Thu, 19 Jan 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Add a mechanism to dispatch and listen to an action
https://bugs.webkit.org/show_bug.cgi?id=167191

Reviewed by Antti Koivisto.

Added the notion of an action to components. Like DOM events, it can be dispatched or listen to.

Also added ComponentBase.prototype.part which finds a sub-component inside a component's shadow tree,
and made ComponentBase.prototype.content take an id to find an element that matches it.

* browser-tests/close-button-tests.js: Added. Tests for CloseButton.
* browser-tests/component-base-tests.js: Added tests for ComponentBase's part(~), content(id), dispatchEvent.
* browser-tests/index.html:
* public/v3/components/base.js:
(ComponentBase): Added this._actionCallbacks, which is a map of an action name to a callback to be invoked.
(ComponentBase.prototype.content): Return an element of the given id if one is specified.
(ComponentBase.prototype.part): Find a component whose element has the matching id.
(ComponentBase.prototype.dispatchAction): Added.
(ComponentBase.prototype.listenToAction): Added.
(ComponentBase.prototype._ensureShadowTree): Call didConstructShadowTree.
(ComponentBase.prototype.didConstructShadowTree): Added.
(ComponentBase.prototype._recursivelyReplaceUnknownElementsByComponents): Copy attributes when instantiating
an element for a component when the browser doesn't support custom elements API.
(ComponentBase.createLink):
(ComponentBase.prototype.createEventHandler): Added.
(ComponentBase.createEventHandler): Renamed from createActionHandler.
* public/v3/components/button-base.js:
(ButtonBase.prototype.didConstructShadowTree): Added. Dispatch &quot;activate&quot; action when the button is clicked.
(ButtonBase.prototype.setCallback): Deleted.
(ButtonBase.htmlTemplate): Use id instead of class so that this.content() can find it.
(ButtonBase.cssTemplate): Updated style rules.
* public/v3/pages/chart-pane.js:
(ChartPane):
(ChartPane.prototype.didConstructShadowTree): Added. Listen to &quot;activate&quot; action on the close button.
(ChartPane.prototype.render): Fixed a bug that we were never calling enqueueToRender on the close button.
(ChartPane.htmlTemplate): Add the id on the close button.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestscomponentbasetestsjs">trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestsindexhtml">trunk/Websites/perf.webkit.org/browser-tests/index.html</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentsbasejs">trunk/Websites/perf.webkit.org/public/v3/components/base.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentsbuttonbasejs">trunk/Websites/perf.webkit.org/public/v3/components/button-base.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3pageschartpanejs">trunk/Websites/perf.webkit.org/public/v3/pages/chart-pane.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestsclosebuttontestsjs">trunk/Websites/perf.webkit.org/browser-tests/close-button-tests.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebsitesperfwebkitorgChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/ChangeLog (210937 => 210938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2017-01-19 21:35:10 UTC (rev 210937)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2017-01-19 22:58:46 UTC (rev 210938)
</span><span class="lines">@@ -1,3 +1,42 @@
</span><ins>+2017-01-18  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        Add a mechanism to dispatch and listen to an action
+        https://bugs.webkit.org/show_bug.cgi?id=167191
+
+        Reviewed by Antti Koivisto.
+
+        Added the notion of an action to components. Like DOM events, it can be dispatched or listen to.
+
+        Also added ComponentBase.prototype.part which finds a sub-component inside a component's shadow tree,
+        and made ComponentBase.prototype.content take an id to find an element that matches it.
+
+        * browser-tests/close-button-tests.js: Added. Tests for CloseButton.
+        * browser-tests/component-base-tests.js: Added tests for ComponentBase's part(~), content(id), dispatchEvent.
+        * browser-tests/index.html:
+        * public/v3/components/base.js:
+        (ComponentBase): Added this._actionCallbacks, which is a map of an action name to a callback to be invoked.
+        (ComponentBase.prototype.content): Return an element of the given id if one is specified.
+        (ComponentBase.prototype.part): Find a component whose element has the matching id.
+        (ComponentBase.prototype.dispatchAction): Added.
+        (ComponentBase.prototype.listenToAction): Added.
+        (ComponentBase.prototype._ensureShadowTree): Call didConstructShadowTree.
+        (ComponentBase.prototype.didConstructShadowTree): Added.
+        (ComponentBase.prototype._recursivelyReplaceUnknownElementsByComponents): Copy attributes when instantiating
+        an element for a component when the browser doesn't support custom elements API.
+        (ComponentBase.createLink):
+        (ComponentBase.prototype.createEventHandler): Added.
+        (ComponentBase.createEventHandler): Renamed from createActionHandler.
+        * public/v3/components/button-base.js:
+        (ButtonBase.prototype.didConstructShadowTree): Added. Dispatch &quot;activate&quot; action when the button is clicked.
+        (ButtonBase.prototype.setCallback): Deleted.
+        (ButtonBase.htmlTemplate): Use id instead of class so that this.content() can find it.
+        (ButtonBase.cssTemplate): Updated style rules.
+        * public/v3/pages/chart-pane.js:
+        (ChartPane):
+        (ChartPane.prototype.didConstructShadowTree): Added. Listen to &quot;activate&quot; action on the close button.
+        (ChartPane.prototype.render): Fixed a bug that we were never calling enqueueToRender on the close button.
+        (ChartPane.htmlTemplate): Add the id on the close button.
+
</ins><span class="cx"> 2017-01-18  Dewei Zhu  &lt;dewei_zhu@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         'buildbot-syncer.js' should be able to determine force build argument from a list of possible repositories.
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestsclosebuttontestsjs"></a>
<div class="addfile"><h4>Added: trunk/Websites/perf.webkit.org/browser-tests/close-button-tests.js (0 => 210938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/close-button-tests.js                                (rev 0)
+++ trunk/Websites/perf.webkit.org/browser-tests/close-button-tests.js        2017-01-19 22:58:46 UTC (rev 210938)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+
+describe('CloseButton', () =&gt; {
+    const scripts = ['instrumentation.js', 'components/base.js', 'components/button-base.js', 'components/close-button.js'];
+
+    it('must dispatch &quot;activate&quot; action when the anchor is clicked', () =&gt; {
+        const context = new BrowsingContext();
+        return context.importScripts(scripts, 'CloseButton').then((CloseButton) =&gt; {
+            const closeButton = new CloseButton;
+            context.document.body.appendChild(closeButton.element());
+
+            closeButton.content().querySelector('a').click();
+
+            let activateCount = 0;
+            closeButton.listenToAction('activate', () =&gt; {
+                activateCount++;
+            });
+            expect(activateCount).toBe(0);
+            closeButton.content().querySelector('a').click();
+            expect(activateCount).toBe(1);
+            closeButton.content().querySelector('a').click();
+            expect(activateCount).toBe(2);
+        });
+    });
+
+});
</ins></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestscomponentbasetestsjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js (210937 => 210938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js        2017-01-19 21:35:10 UTC (rev 210937)
+++ trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js        2017-01-19 22:58:46 UTC (rev 210938)
</span><span class="lines">@@ -7,7 +7,7 @@
</span><span class="cx">         return context.importScript('components/base.js', 'ComponentBase').then((ComponentBase) =&gt; {
</span><span class="cx">             class SomeComponent extends ComponentBase { }
</span><span class="cx">             if (options.htmlTemplate)
</span><del>-                SomeComponent.htmlTemplate = () =&gt; { return '&lt;div style=&quot;height: 10px;&quot;&gt;&lt;/div&gt;'; };
</del><ins>+                SomeComponent.htmlTemplate = () =&gt; { return '&lt;div id=&quot;div&quot; style=&quot;height: 10px;&quot;&gt;&lt;/div&gt;'; };
</ins><span class="cx">             if (options.cssTemplate)
</span><span class="cx">                 SomeComponent.cssTemplate = () =&gt; { return ':host { height: 10px; }'; };
</span><span class="cx"> 
</span><span class="lines">@@ -87,8 +87,84 @@
</span><span class="cx">                 expect(instance.content()).toBe(instance.content());
</span><span class="cx">             });
</span><span class="cx">         });
</span><ins>+
+        it('must return the element matching the id if an id is specified', () =&gt; {
+            return new BrowsingContext().importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) =&gt; {
+                class SomeComponent extends ComponentBase {
+                    static htmlTemplate() { return '&lt;div id=&quot;part1&quot; title=&quot;foo&quot;&gt;&lt;/div&gt;&lt;div id=&quot;part1&quot;&gt;&lt;/div&gt;'; }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                const instance = new SomeComponent;
+                const part1 = instance.content('part1');
+                expect(part1.localName).toBe('div');
+                expect(part1.title).toBe('foo');
+                expect(instance.content('part2')).toBe(null);
+            });
+        });
</ins><span class="cx">     });
</span><span class="cx"> 
</span><ins>+    describe('part()', () =&gt; {
+        it('must create shadow tree', () =&gt; {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) =&gt; {
+                instance.part('foo');
+                expect(hasShadowTree()).toBe(true);
+            });
+        });
+
+        it('must return the component matching the id if an id is specified', () =&gt; {
+            return new BrowsingContext().importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) =&gt; {
+                class SomeComponent extends ComponentBase { }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                class OtherComponent extends ComponentBase {
+                    static htmlTemplate() { return '&lt;some-component id=&quot;foo&quot;&gt;&lt;/some-component&gt;'; }
+                }
+                ComponentBase.defineElement('other-component', OtherComponent);
+
+                const otherComponent = new OtherComponent;
+                const someComponent = otherComponent.part('foo');
+                expect(someComponent).toBeA(SomeComponent);
+                expect(someComponent.element().id).toBe('foo');
+                expect(otherComponent.part('foo')).toBe(someComponent);
+                expect(otherComponent.part('bar')).toBe(null);
+            });
+        });
+    });
+
+    describe('dispatchAction()', () =&gt; {
+        it('must invoke a callback specified in listenToAction', () =&gt; {
+            return new BrowsingContext().importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) =&gt; {
+                class SomeComponent extends ComponentBase { }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                const instance = new SomeComponent;
+
+                const calls = [];
+                instance.listenToAction('action', (...args) =&gt; {
+                    calls.push(args);
+                });
+                const object = {'foo': 1};
+                instance.dispatchAction('action', 'bar', object, 5);
+
+                expect(calls.length).toBe(1);
+                expect(calls[0][0]).toBe('bar');
+                expect(calls[0][1]).toBe(object);
+                expect(calls[0][2]).toBe(5);
+            });
+        });
+
+        it('must not do anything when there are no callbacks', () =&gt; {
+            return new BrowsingContext().importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) =&gt; {
+                class SomeComponent extends ComponentBase { }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                const object = {'foo': 1};
+                (new SomeComponent).dispatchAction('action', 'bar', object, 5);
+            });
+        });
+    });
+
</ins><span class="cx">     describe('enqueueToRender()', () =&gt; {
</span><span class="cx">         it('must not immediately call render()', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><span class="lines">@@ -298,6 +374,36 @@
</span><span class="cx">                 expect(hasShadowTree()).toBe(true);
</span><span class="cx">             }, {htmlTemplate: false, cssTemplate: true});
</span><span class="cx">         });
</span><ins>+
+        it('must invoke didConstructShadowTree after creating the shadow tree', () =&gt; {
+            const context = new BrowsingContext();
+            return context.importScripts(['instrumentation.js', 'components/base.js'], 'ComponentBase').then((ComponentBase) =&gt; {
+                let didConstructShadowTreeCount = 0;
+                let htmlTemplateCount = 0;
+
+                class SomeComponent extends ComponentBase {
+                    didConstructShadowTree()
+                    {
+                        expect(this.content()).toBeA(context.global.ShadowRoot);
+                        didConstructShadowTreeCount++;
+                    }
+
+                    static htmlTemplate()
+                    {
+                        htmlTemplateCount++;
+                        return '';
+                    }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                const instance = new SomeComponent;
+                expect(didConstructShadowTreeCount).toBe(0);
+                expect(htmlTemplateCount).toBe(0);
+                instance.render();
+                expect(didConstructShadowTreeCount).toBe(1);
+                expect(htmlTemplateCount).toBe(1);
+            });
+        });
</ins><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx">     describe('defineElement()', () =&gt; {
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestsindexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/browser-tests/index.html (210937 => 210938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/index.html        2017-01-19 21:35:10 UTC (rev 210937)
+++ trunk/Websites/perf.webkit.org/browser-tests/index.html        2017-01-19 22:58:46 UTC (rev 210938)
</span><span class="lines">@@ -1,6 +1,7 @@
</span><span class="cx"> &lt;!DOCTYPE html&gt;
</span><span class="cx"> &lt;html&gt;
</span><span class="cx"> &lt;head&gt;
</span><ins>+&lt;title&gt;In-Browser Tests for Performance Dashboard&lt;/title&gt;
</ins><span class="cx"> &lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css&quot;&gt;
</span><span class="cx"> &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/mocha/2.2.5/mocha.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/expect/1.20.2/expect.min.js&quot;&gt;&lt;/script&gt;
</span><span class="lines">@@ -13,6 +14,7 @@
</span><span class="cx"> &lt;body&gt;
</span><span class="cx"> &lt;div id=&quot;mocha&quot;&gt;&lt;/div&gt;
</span><span class="cx"> &lt;script src=&quot;component-base-tests.js&quot;&gt;&lt;/script&gt;
</span><ins>+&lt;script src=&quot;close-button-tests.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx"> &lt;script&gt;
</span><span class="cx"> 
</span><span class="cx"> afterEach(() =&gt; {
</span><span class="lines">@@ -47,6 +49,7 @@
</span><span class="cx">                 script.addEventListener('load', resolve);
</span><span class="cx">                 script.addEventListener('error', reject);
</span><span class="cx">                 script.src = '../public/v3/' + path;
</span><ins>+                script.async = false;
</ins><span class="cx">                 doc.body.appendChild(script);
</span><span class="cx">             });
</span><span class="cx">         })).then(() =&gt; {
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentsbasejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/base.js (210937 => 210938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/base.js        2017-01-19 21:35:10 UTC (rev 210937)
+++ trunk/Websites/perf.webkit.org/public/v3/components/base.js        2017-01-19 22:58:46 UTC (rev 210938)
</span><span class="lines">@@ -15,6 +15,7 @@
</span><span class="cx"> 
</span><span class="cx">         this._element = element;
</span><span class="cx">         this._shadow = null;
</span><ins>+        this._actionCallbacks = new Map;
</ins><span class="cx"> 
</span><span class="cx">         if (!window.customElements &amp;&amp; new.target.enqueueToRenderOnResize)
</span><span class="cx">             ComponentBase._connectedComponentToRenderOnResize(this);
</span><span class="lines">@@ -21,12 +22,37 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     element() { return this._element; }
</span><del>-    content()
</del><ins>+    content(id = null)
</ins><span class="cx">     {
</span><span class="cx">         this._ensureShadowTree();
</span><ins>+        if (this._shadow &amp;&amp; id != null)
+            return this._shadow.getElementById(id);
</ins><span class="cx">         return this._shadow;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    part(id)
+    {
+        this._ensureShadowTree();
+        if (!this._shadow)
+            return null;
+        const part = this._shadow.getElementById(id);
+        if (!part)
+            return null;
+        return part.component();
+    }
+
+    dispatchAction(actionName, ...args)
+    {
+        const callback = this._actionCallbacks.get(actionName);
+        if (callback)
+            callback.apply(this, args);
+    }
+
+    listenToAction(actionName, callback)
+    {
+        this._actionCallbacks.set(actionName, callback);
+    }
+
</ins><span class="cx">     render() { this._ensureShadowTree(); }
</span><span class="cx"> 
</span><span class="cx">     enqueueToRender()
</span><span class="lines">@@ -112,10 +138,12 @@
</span><span class="cx">             style.textContent = newTarget.cssTemplate();
</span><span class="cx">             shadow.appendChild(style);
</span><span class="cx">         }
</span><del>-
</del><span class="cx">         this._shadow = shadow;
</span><ins>+        this.didConstructShadowTree();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    didConstructShadowTree() { }
+
</ins><span class="cx">     _recursivelyReplaceUnknownElementsByComponents(parent)
</span><span class="cx">     {
</span><span class="cx">         let nextSibling;
</span><span class="lines">@@ -125,6 +153,12 @@
</span><span class="cx">                 if (elementInterface) {
</span><span class="cx">                     const component = new elementInterface();
</span><span class="cx">                     const newChild = component.element();
</span><ins>+
+                    for (let i = 0; i &lt; child.attributes.length; i++) {
+                        const attr = child.attributes[i];
+                        newChild.setAttribute(attr.name, attr.value);
+                    }
+
</ins><span class="cx">                     parent.replaceChild(newChild, child);
</span><span class="cx">                     child = newChild;
</span><span class="cx">                 }
</span><span class="lines">@@ -231,7 +265,7 @@
</span><span class="cx">         if (typeof(callback) === 'string')
</span><span class="cx">             attributes['href'] = callback;
</span><span class="cx">         else
</span><del>-            attributes['onclick'] = ComponentBase.createActionHandler(callback);
</del><ins>+            attributes['onclick'] = ComponentBase.createEventHandler(callback);
</ins><span class="cx"> 
</span><span class="cx">         if (isExternal)
</span><span class="cx">             attributes['target'] = '_blank';
</span><span class="lines">@@ -238,7 +272,8 @@
</span><span class="cx">         return ComponentBase.createElement('a', attributes, content);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    static createActionHandler(callback)
</del><ins>+    createEventHandler(callback) { return ComponentBase.createEventHandler(callback); }
+    static createEventHandler(callback)
</ins><span class="cx">     {
</span><span class="cx">         return function (event) {
</span><span class="cx">             event.preventDefault();
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentsbuttonbasejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/button-base.js (210937 => 210938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/button-base.js        2017-01-19 21:35:10 UTC (rev 210937)
+++ trunk/Websites/perf.webkit.org/public/v3/components/button-base.js        2017-01-19 22:58:46 UTC (rev 210938)
</span><span class="lines">@@ -1,18 +1,15 @@
</span><span class="cx"> 
</span><span class="cx"> class ButtonBase extends ComponentBase {
</span><del>-    constructor(name)
</del><ins>+    didConstructShadowTree()
</ins><span class="cx">     {
</span><del>-        super(name);
</del><ins>+        this.content('button').addEventListener('click', this.createEventHandler(() =&gt; {
+            this.dispatchAction('activate');
+        }));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    setCallback(callback)
-    {
-        this.content().querySelector('a').addEventListener('click', ComponentBase.createActionHandler(callback));
-    }
-
</del><span class="cx">     static htmlTemplate()
</span><span class="cx">     {
</span><del>-        return `&lt;a class=&quot;button&quot; href=&quot;#&quot;&gt;&lt;svg viewBox=&quot;0 0 100 100&quot;&gt;${this.buttonContent()}&lt;/svg&gt;&lt;/a&gt;`;
</del><ins>+        return `&lt;a id=&quot;button&quot; href=&quot;#&quot;&gt;&lt;svg viewBox=&quot;0 0 100 100&quot;&gt;${this.buttonContent()}&lt;/svg&gt;&lt;/a&gt;`;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     static buttonContent() { throw 'NotImplemented'; }
</span><span class="lines">@@ -28,18 +25,18 @@
</span><span class="cx">                 height: ${sizeFactor}rem;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            .button {
</del><ins>+            a {
</ins><span class="cx">                 vertical-align: bottom;
</span><span class="cx">                 display: block;
</span><span class="cx">                 opacity: 0.3;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            .button svg {
-                display: block;
</del><ins>+            a:hover {
+                opacity: 0.6;
</ins><span class="cx">             }
</span><span class="cx"> 
</span><del>-            .button:hover {
-                opacity: 0.6;
</del><ins>+            svg {
+                display: block;
</ins><span class="cx">             }
</span><span class="cx">         `;
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3pageschartpanejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/pages/chart-pane.js (210937 => 210938)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/pages/chart-pane.js        2017-01-19 21:35:10 UTC (rev 210937)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/chart-pane.js        2017-01-19 22:58:46 UTC (rev 210938)
</span><span class="lines">@@ -77,11 +77,16 @@
</span><span class="cx">         this._trendLineVersion = 0;
</span><span class="cx">         this._renderedTrendLineOptions = false;
</span><span class="cx"> 
</span><del>-        this.content().querySelector('close-button').component().setCallback(chartsPage.closePane.bind(chartsPage, this));
-
</del><span class="cx">         this.configure(platformId, metricId);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    didConstructShadowTree()
+    {
+        this.part('close').listenToAction('activate', () =&gt; {
+            this._chartsPage.closePane(this);
+        })
+    }
+
</ins><span class="cx">     serializeState()
</span><span class="cx">     {
</span><span class="cx">         var state = [this._platformId, this._metricId];
</span><span class="lines">@@ -252,6 +257,8 @@
</span><span class="cx">         var link = ComponentBase.createLink;
</span><span class="cx">         var self = this;
</span><span class="cx"> 
</span><ins>+        this.part('close').enqueueToRender();
+
</ins><span class="cx">         if (this._chartsPage.canBreakdown(platform, metric)) {
</span><span class="cx">             actions.push(element('li', link('Breakdown', function () {
</span><span class="cx">                 self._chartsPage.insertBreakdownPanesAfter(platform, metric, self);
</span><span class="lines">@@ -514,7 +521,7 @@
</span><span class="cx">                 &lt;h2 class=&quot;chart-pane-title&quot;&gt;-&lt;/h2&gt;
</span><span class="cx">                 &lt;nav class=&quot;chart-pane-actions&quot;&gt;
</span><span class="cx">                     &lt;ul&gt;
</span><del>-                        &lt;li class=&quot;close&quot;&gt;&lt;close-button&gt;&lt;/close-button&gt;&lt;/li&gt;
</del><ins>+                        &lt;li&gt;&lt;close-button id=&quot;close&quot;&gt;&lt;/close-button&gt;&lt;/li&gt;
</ins><span class="cx">                     &lt;/ul&gt;
</span><span class="cx">                     &lt;ul class=&quot;chart-pane-action-buttons buttoned-toolbar&quot;&gt;&lt;/ul&gt;
</span><span class="cx">                     &lt;ul class=&quot;chart-pane-alternative-platforms popover&quot; style=&quot;display:none&quot;&gt;&lt;/ul&gt;
</span></span></pre>
</div>
</div>

</body>
</html>