Title: [210783] trunk/Websites/perf.webkit.org
Revision
210783
Author
rn...@webkit.org
Date
2017-01-15 16:12:22 -0800 (Sun, 15 Jan 2017)

Log Message

Adopt custom elements API in perf dashboard
https://bugs.webkit.org/show_bug.cgi?id=167045

Reviewed by Darin Adler.

Adopt custom elements API in ComponentBase, and create the shadow tree lazily in content() and render()
instead of eagerly creating it inside the constructor.

For now, create a separate element class for each component in ComponentBase.defineElement instead of
making ComponentBase inherit from HTMLElement to preserve the semantics we have as well as to test
the boundaries of what custom elements API allows for framework authors.

In order to ensure one-to-one correspondence between elements and their components, we use a static map,
ComponentBase._currentlyConstructedByInterface, to remember which element or component is being created
and use that in custom element's constructor to update element.component() and this._element.

Also dropped the support for not having attachShadow as we've shipped this feature in Safari 10.

Finally, added tests to be ran inside a browser to test the front end code in browser-tests.

* browser-tests/component-base-tests.js: Added. Basic tests for ComponentBase.
* browser-tests/index.html: Added.

* public/v3/components/base.js:
(ComponentBase): Don't create the shadow tree. Use the currently constructed element as this._element if
there is one (the custom element's constructor is getting called). Otherwise create a new element but
store this component in the map to avoid creating a new component in the custom element's constructor.
(ComponentBase.prototype.content): Lazily create the shadow tree now.
(ComponentBase.prototype.render): Ditto.
(ComponentBase.prototype._ensureShadowTree): Renamed from _constructShadowTree. Dropped the support for
not having shadow DOM API. This is now required. Also use importNode instead of cloneNode in cloning
the template content since the latter would not get upgraded.
(ComponentBase.prototype._recursivelyReplaceUnknownElementsByComponents): Modernized the code. Don't
re-create a component if its element had already been upgraded by its custom element constructor.
(ComponentBase.defineElement): Add this component to the static maps. _componentByName is used by
_recursivelyReplaceUnknownElementsByComponents to instantiate new components in the browsers that don't
support custom elements API and _componentByClass is used by ComponentBase's constructor to lookup the
element name. The latter should go away once all components fully adopt ComponentBase.defineElement.
(ComponentBase.defineElement.elementClass): A class to define a custom element for the component.
We need to reconfigure the property since class's name is not writable but configurable.

* public/v3/components/button-base.js:
(ButtonBase.htmlTemplate): Added. Extracted the common code from CloseButton and WarningIcon.
(ButtonBase.buttonContent): Added. An abstract method overridden by CloseButton and WarningIcon.
(ButtonBase.sizeFactor): Added. Overridden by WarningIcon.
(ButtonBase.cssTemplate): Updated to use :host.
* public/v3/components/close-button.js:
(CloseButton.buttonContent): Renamed from htmlTemplate.
* public/v3/components/spinner-icon.js:
(SpinnerIcon.cssTemplate): Removed webkit prefixed properties, and updated it to animate stroke instead
of opacity to reduce the power usage.
(SpinnerIcon.htmlTemplate): Factored stroke, stroke-width, and stroke-linecap into cssTemplate.
* public/v3/components/warning-icon.js:
(WarningIcon.cssTemplate): Deleted.
(WarningIcon.sizeFactor): Added.
(WarningIcon.buttonContent): Renamed from htmlTemplate.
* public/v3/pages/summary-page.js:
(SummaryPage._constructRatioGraph): Fixed a bug that we were not never calling spinner.updateRendering().
(SummaryPage.prototype._renderCell):

Modified Paths

Added Paths

Diff

Modified: trunk/Websites/perf.webkit.org/ChangeLog (210782 => 210783)


--- trunk/Websites/perf.webkit.org/ChangeLog	2017-01-15 22:42:53 UTC (rev 210782)
+++ trunk/Websites/perf.webkit.org/ChangeLog	2017-01-16 00:12:22 UTC (rev 210783)
@@ -1,3 +1,65 @@
+2017-01-12  Ryosuke Niwa  <rn...@webkit.org>
+
+        Adopt custom elements API in perf dashboard
+        https://bugs.webkit.org/show_bug.cgi?id=167045
+
+        Reviewed by Darin Adler.
+
+        Adopt custom elements API in ComponentBase, and create the shadow tree lazily in content() and render()
+        instead of eagerly creating it inside the constructor.
+
+        For now, create a separate element class for each component in ComponentBase.defineElement instead of
+        making ComponentBase inherit from HTMLElement to preserve the semantics we have as well as to test
+        the boundaries of what custom elements API allows for framework authors.
+
+        In order to ensure one-to-one correspondence between elements and their components, we use a static map,
+        ComponentBase._currentlyConstructedByInterface, to remember which element or component is being created
+        and use that in custom element's constructor to update element.component() and this._element.
+
+        Also dropped the support for not having attachShadow as we've shipped this feature in Safari 10.
+
+        Finally, added tests to be ran inside a browser to test the front end code in browser-tests.
+
+        * browser-tests/component-base-tests.js: Added. Basic tests for ComponentBase.
+        * browser-tests/index.html: Added.
+
+        * public/v3/components/base.js:
+        (ComponentBase): Don't create the shadow tree. Use the currently constructed element as this._element if
+        there is one (the custom element's constructor is getting called). Otherwise create a new element but
+        store this component in the map to avoid creating a new component in the custom element's constructor.
+        (ComponentBase.prototype.content): Lazily create the shadow tree now.
+        (ComponentBase.prototype.render): Ditto.
+        (ComponentBase.prototype._ensureShadowTree): Renamed from _constructShadowTree. Dropped the support for
+        not having shadow DOM API. This is now required. Also use importNode instead of cloneNode in cloning
+        the template content since the latter would not get upgraded.
+        (ComponentBase.prototype._recursivelyReplaceUnknownElementsByComponents): Modernized the code. Don't
+        re-create a component if its element had already been upgraded by its custom element constructor.
+        (ComponentBase.defineElement): Add this component to the static maps. _componentByName is used by
+        _recursivelyReplaceUnknownElementsByComponents to instantiate new components in the browsers that don't
+        support custom elements API and _componentByClass is used by ComponentBase's constructor to lookup the
+        element name. The latter should go away once all components fully adopt ComponentBase.defineElement.
+        (ComponentBase.defineElement.elementClass): A class to define a custom element for the component.
+        We need to reconfigure the property since class's name is not writable but configurable.
+
+        * public/v3/components/button-base.js:
+        (ButtonBase.htmlTemplate): Added. Extracted the common code from CloseButton and WarningIcon.
+        (ButtonBase.buttonContent): Added. An abstract method overridden by CloseButton and WarningIcon.
+        (ButtonBase.sizeFactor): Added. Overridden by WarningIcon.
+        (ButtonBase.cssTemplate): Updated to use :host.
+        * public/v3/components/close-button.js:
+        (CloseButton.buttonContent): Renamed from htmlTemplate.
+        * public/v3/components/spinner-icon.js:
+        (SpinnerIcon.cssTemplate): Removed webkit prefixed properties, and updated it to animate stroke instead
+        of opacity to reduce the power usage.
+        (SpinnerIcon.htmlTemplate): Factored stroke, stroke-width, and stroke-linecap into cssTemplate.
+        * public/v3/components/warning-icon.js:
+        (WarningIcon.cssTemplate): Deleted.
+        (WarningIcon.sizeFactor): Added.
+        (WarningIcon.buttonContent): Renamed from htmlTemplate.
+        * public/v3/pages/summary-page.js:
+        (SummaryPage._constructRatioGraph): Fixed a bug that we were not never calling spinner.updateRendering().
+        (SummaryPage.prototype._renderCell):
+
 2017-01-13  Ryosuke Niwa  <rn...@webkit.org>
 
         Instrument calls to render()

Added: trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js (0 => 210783)


--- trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js	                        (rev 0)
+++ trunk/Websites/perf.webkit.org/browser-tests/component-base-tests.js	2017-01-16 00:12:22 UTC (rev 210783)
@@ -0,0 +1,184 @@
+
+describe('ComponentBase', function() {
+
+    function createTestToCheckExistenceOfShadowTree(callback, options = {htmlTemplate: false, cssTemplate: true})
+    {
+        const context = new BrowsingContext();
+        return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+            class SomeComponent extends ComponentBase { }
+            if (options.htmlTemplate)
+                SomeComponent.htmlTemplate = () => { return '<div style="height: 10px;"></div>'; };
+            if (options.cssTemplate)
+                SomeComponent.cssTemplate = () => { return ':host { height: 10px; }'; };
+
+            let instance = new SomeComponent('some-component');
+            instance.element().style.display = 'block';
+            context.document.body.appendChild(instance.element());
+            return callback(instance, () => { return instance.element().offsetHeight == 10; });
+        });
+    }
+
+    describe('constructor', () => {
+        it('is a function', () => {
+            return new BrowsingContext().importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+                expect(ComponentBase).toBeA('function');
+            });
+        });
+
+        it('can be instantiated', () => {
+            return new BrowsingContext().importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+                let callCount = 0;
+                class SomeComponent extends ComponentBase {
+                    constructor() {
+                        super('some-component');
+                        callCount++;
+                    }
+                }
+                let instance = new SomeComponent;
+                expect(instance).toBeA(ComponentBase);
+                expect(instance).toBeA(SomeComponent);
+                expect(callCount).toBe(1);
+            });
+        });
+
+        it('must not create shadow tree eagerly', () => {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
+                expect(hasShadowTree()).toBe(false);
+            });
+        });
+    });
+
+    describe('element()', () => {
+        it('must return an element', () => {
+            const context = new BrowsingContext();
+            return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+                class SomeComponent extends ComponentBase { }
+                let instance = new SomeComponent('some-component');
+                expect(instance.element()).toBeA(context.global.HTMLElement);
+            });
+        });
+
+        it('must return an element whose component() matches the component', () => {
+            return new BrowsingContext().importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+                class SomeComponent extends ComponentBase { }
+                let instance = new SomeComponent('some-component');
+                expect(instance.element().component()).toBe(instance);
+            });
+        });
+
+        it('must not create shadow tree eagerly', () => {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
+                instance.element();
+                expect(hasShadowTree()).toBe(false);
+            });
+        });
+    });
+
+    describe('content()', () => {
+        it('must create shadow tree', () => {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
+                instance.content();
+                expect(hasShadowTree()).toBe(true);
+            });
+        });
+
+        it('must return the same shadow tree each time it is called', () => {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
+                expect(instance.content()).toBe(instance.content());
+            });
+        });
+    });
+
+    describe('render()', () => {
+        it('must create shadow tree', () => {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
+                instance.render();
+                expect(hasShadowTree()).toBe(true);
+            });
+        });
+
+        it('must not create shadow tree when neither htmlTemplate nor cssTemplate are present', () => {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
+                instance.render();
+                expect(hasShadowTree()).toBe(false);
+            }, {htmlTemplate: false, cssTemplate: false});
+        });
+
+        it('must create shadow tree when htmlTemplate is present and cssTemplate is not', () => {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
+                instance.render();
+                expect(hasShadowTree()).toBe(true);
+            }, {htmlTemplate: true, cssTemplate: false});
+        });
+
+        it('must create shadow tree when cssTemplate is present and htmlTemplate is not', () => {
+            return createTestToCheckExistenceOfShadowTree((instance, hasShadowTree) => {
+                instance.render();
+                expect(hasShadowTree()).toBe(true);
+            }, {htmlTemplate: false, cssTemplate: true});
+        });
+    });
+
+    describe('defineElement()', () => {
+
+        it('must define a custom element with a class of an appropriate name', () => {
+            const context = new BrowsingContext();
+            return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+                class SomeComponent extends ComponentBase { }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                let elementClass = context.global.customElements.get('some-component');
+                expect(elementClass).toBeA('function');
+                expect(elementClass.name).toBe('SomeComponentElement');
+            });
+        });
+
+        it('must define a custom element that can be instantiated via document.createElement', () => {
+            const context = new BrowsingContext();
+            return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+                let instances = [];
+                class SomeComponent extends ComponentBase {
+                    constructor() {
+                        super();
+                        instances.push(this);
+                    }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                expect(instances.length).toBe(0);
+                let element = context.document.createElement('some-component');
+                expect(instances.length).toBe(1);
+
+                expect(element).toBeA(context.global.HTMLElement);
+                expect(element.component()).toBe(instances[0]);
+                expect(instances[0].element()).toBe(element);
+                expect(instances.length).toBe(1);
+            });
+        });
+
+        it('must define a custom element that can be instantiated via new', () => {
+            const context = new BrowsingContext();
+            return context.importScript('../public/v3/components/base.js', 'ComponentBase').then((ComponentBase) => {
+                let instances = [];
+                class SomeComponent extends ComponentBase {
+                    constructor() {
+                        super();
+                        instances.push(this);
+                    }
+                }
+                ComponentBase.defineElement('some-component', SomeComponent);
+
+                expect(instances.length).toBe(0);
+                let component = new SomeComponent;
+                expect(instances.length).toBe(1);
+
+                expect(component).toBe(instances[0]);
+                expect(component.element()).toBeA(context.global.HTMLElement);
+                expect(component.element().component()).toBe(component);
+                expect(instances.length).toBe(1);
+            });
+        });
+
+    });
+
+});

Added: trunk/Websites/perf.webkit.org/browser-tests/index.html (0 => 210783)


--- trunk/Websites/perf.webkit.org/browser-tests/index.html	                        (rev 0)
+++ trunk/Websites/perf.webkit.org/browser-tests/index.html	2017-01-16 00:12:22 UTC (rev 210783)
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href=""
+<script src=""
+<script src=""
+<script>
+
+mocha.setup('bdd');
+
+</script>
+</head>
+<body>
+<div id="mocha"></div>
+<script src=""
+<script>
+
+afterEach(() => {
+    BrowsingContext.cleanup();
+});
+
+class BrowsingContext {
+
+    constructor()
+    {
+        let iframe = document.createElement('iframe');
+        document.body.appendChild(iframe);
+        iframe.style.position = 'absolute';
+        iframe.style.left = '0px';
+        iframe.style.top = '0px';
+        BrowsingContext._iframes.push(iframe);
+
+        this._iframe = iframe;
+        this.symbols = {};
+        this.global = this._iframe.contentWindow;
+        this.document = this._iframe.contentDocument;
+    }
+
+    importScript(path, ...symbolList)
+    {
+        const doc = this._iframe.contentDocument;
+        const global = this._iframe.contentWindow;
+        return new Promise((resolve, reject) => {
+            let script = doc.createElement('script');
+            script.addEventListener('load', resolve);
+            script.addEventListener('error', reject);
+            script.src = ""
+            doc.body.appendChild(script);
+        }).then(() => {
+            const script = doc.createElement('script');
+            script.textContent = `window.importedSymbols = [${symbolList.join(', ')}];`;
+            doc.body.appendChild(script);
+
+            const importedSymbols = global.importedSymbols;
+            for (let i = 0; i < symbolList.length; i++)
+                this.symbols[symbolList[i]] = importedSymbols[i];
+
+            return symbolList.length == 1 ? importedSymbols[0] : importedSymbols;
+        });
+    }
+
+    static cleanup()
+    {
+        BrowsingContext._iframes.forEach((iframe) => { iframe.remove(); });
+        BrowsingContext._iframes = [];
+    }
+
+    static createWithScripts(scriptList)
+    {
+        let iframe = document.createElement('iframe');
+        document.body.appendChild(iframe);
+        const doc = iframe.contentDocument;
+
+        let symbolList = [];
+        return Promise.all(scriptList.map((entry) => {
+            let [path, ...symbols] = entry;
+            symbolList = symbolList.concat(symbols);
+            return new Promise((resolve, reject) => {
+                let script = doc.createElement('script');
+                script.addEventListener('load', resolve);
+                script.addEventListener('error', reject);
+                script.src = ""
+                doc.body.appendChild(script);
+            });
+        })).then(() => {
+            const script = doc.createElement('script');
+            script.textContent = `var symbols = { ${symbolList.join(', ')} };`;
+            doc.body.appendChild(script);
+            return iframe.contentWindow;
+        });
+    }
+}
+BrowsingContext._iframes = [];
+
+mocha.checkLeaks();
+mocha.globals(['expect', 'BrowsingContext']);
+mocha.run();
+
+</script>
+</body>
+</html>

Modified: trunk/Websites/perf.webkit.org/public/v3/components/base.js (210782 => 210783)


--- trunk/Websites/perf.webkit.org/public/v3/components/base.js	2017-01-15 22:42:53 UTC (rev 210782)
+++ trunk/Websites/perf.webkit.org/public/v3/components/base.js	2017-01-16 00:12:22 UTC (rev 210783)
@@ -1,17 +1,31 @@
 
-// FIXME: ComponentBase should inherit from HTMLElement when custom elements API is available.
 class ComponentBase {
     constructor(name)
     {
-        this._element = document.createElement(name);
-        this._element.component = (function () { return this; }).bind(this);
-        this._shadow = this._constructShadowTree();
+        this._componentName = name || ComponentBase._componentByClass.get(new.target);
+
+        const currentlyConstructed = ComponentBase._currentlyConstructedByInterface;
+        let element = currentlyConstructed.get(new.target);
+        if (!element) {
+            currentlyConstructed.set(new.target, this);
+            element = document.createElement(this._componentName);
+            currentlyConstructed.delete(new.target);
+        }
+        element.component = () => { return this };
+
+        this._element = element;
+        this._shadow = null;
     }
 
     element() { return this._element; }
-    content() { return this._shadow; }
-    render() { }
+    content()
+    {
+        this._ensureShadowTree();
+        return this._shadow;
+    }
 
+    render() { this._ensureShadowTree(); }
+
     updateRendering()
     {
         Instrumentation.startMeasuringTime('ComponentBase', 'updateRendering');
@@ -28,52 +42,45 @@
             ComponentBase._addContentToElement(element, content);
     }
 
-    _constructShadowTree()
+    _ensureShadowTree()
     {
-        var newTarget = this.__proto__.constructor;
+        if (this._shadow)
+            return;
 
-        var htmlTemplate = newTarget['htmlTemplate'];
-        var cssTemplate = newTarget['cssTemplate'];
+        const newTarget = this.__proto__.constructor;
+        const htmlTemplate = newTarget['htmlTemplate'];
+        const cssTemplate = newTarget['cssTemplate'];
 
         if (!htmlTemplate && !cssTemplate)
-            return null;
+            return;
 
-        var shadow = null;
-        if ('attachShadow' in Element.prototype)
-            shadow = this._element.attachShadow({mode: 'closed'});
-        else if ('createShadowRoot' in Element.prototype) // Legacy Chromium API.
-            shadow = this._element.createShadowRoot();
-        else
-            shadow = this._element;
+        const shadow = this._element.attachShadow({mode: 'closed'});
 
         if (htmlTemplate) {
-            var template = document.createElement('template');
+            const template = document.createElement('template');
             template.innerHTML = newTarget.htmlTemplate();
-            shadow.appendChild(template.content.cloneNode(true));
+            shadow.appendChild(document.importNode(template.content, true));
             this._recursivelyReplaceUnknownElementsByComponents(shadow);
         }
 
         if (cssTemplate) {
-            var style = document.createElement('style');
+            const style = document.createElement('style');
             style.textContent = newTarget.cssTemplate();
             shadow.appendChild(style);
         }
 
-        return shadow;
+        this._shadow = shadow;
     }
 
     _recursivelyReplaceUnknownElementsByComponents(parent)
     {
-        if (!ComponentBase._map)
-            return;
-
-        var nextSibling;
-        for (var child = parent.firstChild; child; child = child.nextSibling) {
-            if (child instanceof HTMLUnknownElement || child instanceof HTMLElement) {
-                var elementInterface = ComponentBase._map[child.localName];
+        let nextSibling;
+        for (let child = parent.firstChild; child; child = child.nextSibling) {
+            if (child instanceof HTMLElement && !child.component) {
+                const elementInterface = ComponentBase._componentByName.get(child.localName);
                 if (elementInterface) {
-                    var component = new elementInterface();
-                    var newChild = component.element();
+                    const component = new elementInterface();
+                    const newChild = component.element();
                     parent.replaceChild(newChild, child);
                     child = newChild;
                 }
@@ -84,9 +91,30 @@
 
     static defineElement(name, elementInterface)
     {
-        if (!ComponentBase._map)
-            ComponentBase._map = {};
-        ComponentBase._map[name] = elementInterface;
+        ComponentBase._componentByName.set(name, elementInterface);
+        ComponentBase._componentByClass.set(elementInterface, name);
+
+        class elementClass extends HTMLElement {
+            constructor()
+            {
+                super();
+
+                const currentlyConstructed = ComponentBase._currentlyConstructedByInterface;
+                const component = currentlyConstructed.get(elementInterface);
+                if (component)
+                    return; // ComponentBase's constructor will take care of the rest.
+
+                currentlyConstructed.set(elementInterface, this);
+                new elementInterface();
+                currentlyConstructed.delete(elementInterface);
+            }
+        }
+
+        const nameDescriptor = Object.getOwnPropertyDescriptor(elementClass, 'name');
+        nameDescriptor.value = `${elementInterface.name}Element`;
+        Object.defineProperty(elementClass, 'name', nameDescriptor);
+
+        customElements.define(name, elementClass);
     }
 
     static createElement(name, attributes, content)
@@ -159,6 +187,10 @@
     }
 }
 
+ComponentBase._componentByName = new Map;
+ComponentBase._componentByClass = new Map;
+ComponentBase._currentlyConstructedByInterface = new Map;
+
 ComponentBase.css = Symbol();
 ComponentBase.html = Symbol();
 ComponentBase.map = {};

Modified: trunk/Websites/perf.webkit.org/public/v3/components/button-base.js (210782 => 210783)


--- trunk/Websites/perf.webkit.org/public/v3/components/button-base.js	2017-01-15 22:42:53 UTC (rev 210782)
+++ trunk/Websites/perf.webkit.org/public/v3/components/button-base.js	2017-01-16 00:12:22 UTC (rev 210783)
@@ -10,21 +10,37 @@
         this.content().querySelector('a').addEventListener('click', ComponentBase.createActionHandler(callback));
     }
 
+    static htmlTemplate()
+    {
+        return `<a class="button" href="" viewBox="0 0 100 100">${this.buttonContent()}</svg></a>`;
+    }
+
+    static buttonContent() { throw 'NotImplemented'; }
+    static sizeFactor() { return 1; }
+
     static cssTemplate()
     {
+        const sizeFactor = this.sizeFactor();
         return `
+            :host {
+                display: inline-block;
+                width: ${sizeFactor}rem;
+                height: ${sizeFactor}rem;
+            }
+
             .button {
                 vertical-align: bottom;
-                display: inline-block;
-                width: 1rem;
-                height: 1rem;
+                display: block;
                 opacity: 0.3;
             }
 
+            .button svg {
+                display: block;
+            }
+
             .button:hover {
                 opacity: 0.6;
             }
         `;
     }
-
 }

Modified: trunk/Websites/perf.webkit.org/public/v3/components/close-button.js (210782 => 210783)


--- trunk/Websites/perf.webkit.org/public/v3/components/close-button.js	2017-01-15 22:42:53 UTC (rev 210782)
+++ trunk/Websites/perf.webkit.org/public/v3/components/close-button.js	2017-01-16 00:12:22 UTC (rev 210783)
@@ -5,16 +5,13 @@
         super('close-button');
     }
 
-    static htmlTemplate()
+    static buttonContent()
     {
-        return `
-            <a class="button" href="" viewBox="0 0 100 100">
-                <g stroke="black" stroke-width="10">
-                    <circle cx="50" cy="50" r="45" fill="transparent"/>
-                    <polygon points="30,30 70,70" />
-                    <polygon points="30,70 70,30" />
-                </g>
-            </svg></a>`;
+        return `<g stroke="black" stroke-width="10">
+            <circle cx="50" cy="50" r="45" fill="transparent"/>
+            <polygon points="30,30 70,70" />
+            <polygon points="30,70 70,30" />
+        </g>`;
     }
 }
 

Modified: trunk/Websites/perf.webkit.org/public/v3/components/spinner-icon.js (210782 => 210783)


--- trunk/Websites/perf.webkit.org/public/v3/components/spinner-icon.js	2017-01-15 22:42:53 UTC (rev 210782)
+++ trunk/Websites/perf.webkit.org/public/v3/components/spinner-icon.js	2017-01-16 00:12:22 UTC (rev 210783)
@@ -8,76 +8,43 @@
     static cssTemplate()
     {
         return `
-        .spinner {
+        :host {
+            display: inline-block;
             width: 2rem;
             height: 2rem;
-            -webkit-transform: translateZ(0);
+            will-change: opacity; /* Threre is no will-change: stroke. */
         }
-        .spinner line {
+        line {
             animation: spinner-animation 1.6s linear infinite;
-            -webkit-animation: spinner-animation 1.6s linear infinite;
-            opacity: 0.1;
+            stroke: rgb(230, 230, 230);
+            stroke-width: 10;
+            stroke-linecap: round;
         }
-        .spinner line:nth-child(0) {
-            -webkit-animation-delay: 0.0s;
-            animation-delay: 0.0s;
-        }
-        .spinner line:nth-child(1) {
-            -webkit-animation-delay: 0.2s;
-            animation-delay: 0.2s;
-        }
-        .spinner line:nth-child(2) {
-            -webkit-animation-delay: 0.4s;
-            animation-delay: 0.4s;
-        }
-        .spinner line:nth-child(3) {
-            -webkit-animation-delay: 0.6s;
-            animation-delay: 0.6s;
-        }
-        .spinner line:nth-child(4) {
-            -webkit-animation-delay: 0.8s;
-            animation-delay: 0.8s;
-        }
-        .spinner line:nth-child(5) {
-            -webkit-animation-delay: 1s;
-            animation-delay: 1s;
-        }
-        .spinner line:nth-child(6) {
-            -webkit-animation-delay: 1.2s;
-            animation-delay: 1.2s;
-        }
-        .spinner line:nth-child(7) {
-            -webkit-animation-delay: 1.4s;
-            animation-delay: 1.4s;
-        }
-        .spinner line:nth-child(8) {
-            -webkit-animation-delay: 1.6s;
-            animation-delay: 1.6s;
-        }
+        line:nth-child(0) { animation-delay: 0.0s; }
+        line:nth-child(1) { animation-delay: 0.2s; }
+        line:nth-child(2) { animation-delay: 0.4s; }
+        line:nth-child(3) { animation-delay: 0.6s; }
+        line:nth-child(4) { animation-delay: 0.8s; }
+        line:nth-child(5) { animation-delay: 1.0s; }
+        line:nth-child(6) { animation-delay: 1.2s; }
+        line:nth-child(7) { animation-delay: 1.4s; }
         @keyframes spinner-animation {
-            0% { opacity: 0.9; }
-            50% { opacity: 0.1; }
-            100% { opacity: 0.1; }
-        }
-        @-webkit-keyframes spinner-animation {
-            0% { opacity: 0.9; }
-            50% { opacity: 0.1; }
-            100% { opacity: 0.1; }
-        }
-        `;
+            0% { stroke: rgb(25, 25, 25); }
+            50% { stroke: rgb(230, 230, 230); }
+        }`;
     }
 
     static htmlTemplate()
     {
         return `<svg class="spinner" viewBox="0 0 100 100">
-            <line x1="10" y1="50" x2="30" y2="50" stroke="black" stroke-width="10" stroke-linecap="round"/>
-            <line x1="21.72" y1="21.72" x2="35.86" y2="35.86" stroke="black" stroke-width="10" stroke-linecap="round"/>
-            <line x1="50" y1="10" x2="50" y2="30" stroke="black" stroke-width="10" stroke-linecap="round"/>
-            <line x1="78.28" y1="21.72" x2="64.14" y2="35.86" stroke="black" stroke-width="10" stroke-linecap="round"/>
-            <line x1="70" y1="50" x2="90" y2="50" stroke="black" stroke-width="10" stroke-linecap="round"/>
-            <line x1="65.86" y1="65.86" x2="78.28" y2="78.28" stroke="black" stroke-width="10" stroke-linecap="round"/>
-            <line x1="50" y1="70" x2="50" y2="90" stroke="black" stroke-width="10" stroke-linecap="round"/>
-            <line x1="21.72" y1="78.28" x2="35.86" y2="65.86" stroke="black" stroke-width="10" stroke-linecap="round"/>
+            <line x1="10" y1="50" x2="30" y2="50"/>
+            <line x1="21.72" y1="21.72" x2="35.86" y2="35.86"/>
+            <line x1="50" y1="10" x2="50" y2="30"/>
+            <line x1="78.28" y1="21.72" x2="64.14" y2="35.86"/>
+            <line x1="70" y1="50" x2="90" y2="50"/>
+            <line x1="65.86" y1="65.86" x2="78.28" y2="78.28"/>
+            <line x1="50" y1="70" x2="50" y2="90"/>
+            <line x1="21.72" y1="78.28" x2="35.86" y2="65.86"/>
         </svg>`;
     }
 

Modified: trunk/Websites/perf.webkit.org/public/v3/components/warning-icon.js (210782 => 210783)


--- trunk/Websites/perf.webkit.org/public/v3/components/warning-icon.js	2017-01-15 22:42:53 UTC (rev 210782)
+++ trunk/Websites/perf.webkit.org/public/v3/components/warning-icon.js	2017-01-16 00:12:22 UTC (rev 210783)
@@ -5,29 +5,14 @@
         super('warning-icon');
     }
 
-    static cssTemplate()
-    {
-        return super.cssTemplate() + `
-            .button {
-                display: block;
-                width: 0.7rem;
-                height: 0.7rem;
-            }
-            .button svg {
-                display: block;
-            }
-        `;
-    }
+    static sizeFactor() { return 0.7; }
 
-    static htmlTemplate()
+    static buttonContent()
     {
-        return `<a class="button" href="" viewBox="0 0 100 100">
-            <g stroke="#9f6000" fill="#9f6000" stroke-width="7">
+        return `<g stroke="#9f6000" fill="#9f6000" stroke-width="7">
                 <polygon points="0,0, 100,0, 0,100" />
-            </g>
-        </svg></a>`;
+            </g>`;
     }
-
 }
 
 ComponentBase.defineElement('warning-icon', WarningIcon);

Modified: trunk/Websites/perf.webkit.org/public/v3/main.js (210782 => 210783)


--- trunk/Websites/perf.webkit.org/public/v3/main.js	2017-01-15 22:42:53 UTC (rev 210782)
+++ trunk/Websites/perf.webkit.org/public/v3/main.js	2017-01-16 00:12:22 UTC (rev 210783)
@@ -7,6 +7,17 @@
 }
 
 function main() {
+    const requriedFeatures = {
+        'Custom Elements API': () => { return !!window.customElements; },
+        'Shadow DOM API': () => { return !!Element.prototype.attachShadow; },
+        'Latest DOM': () => { return !!Element.prototype.getRootNode; },
+    };
+
+    for (let name in requriedFeatures) {
+        if (!requriedFeatures[name]())
+            return alert(`Your browser does not support ${name}. Try using the latest Safari or Chrome.`);
+    }
+
     (new SpinningPage).open();
 
     Manifest.fetch().then(function (manifest) {

Modified: trunk/Websites/perf.webkit.org/public/v3/pages/summary-page.js (210782 => 210783)


--- trunk/Websites/perf.webkit.org/public/v3/pages/summary-page.js	2017-01-15 22:42:53 UTC (rev 210782)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/summary-page.js	2017-01-16 00:12:22 UTC (rev 210783)
@@ -112,14 +112,17 @@
 
         var state = ChartsPage.createStateForConfigurationList(configurationList);
         var anchor = link(ratioGraph, this.router().url('charts', state));
-        var cell = element('td', [anchor, new SpinnerIcon]);
+        var spinner = new SpinnerIcon;
+        var cell = element('td', [anchor, spinner]);
 
-        this._renderQueue.push(this._renderCell.bind(this, cell, anchor, ratioGraph, configurationGroup));
+        this._renderQueue.push(this._renderCell.bind(this, cell, spinner, anchor, ratioGraph, configurationGroup));
         return cell;
     }
 
-    _renderCell(cell, anchor, ratioGraph, configurationGroup)
+    _renderCell(cell, spinner, anchor, ratioGraph, configurationGroup)
     {
+        spinner.updateRendering();
+
         if (configurationGroup.isFetching())
             cell.classList.add('fetching');
         else
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to