Perf dashboard can consume 50-70% of CPU on MacBook even if user is not interacting...
[WebKit.git] / Websites / perf.webkit.org / public / v3 / components / base.js
1
2 // FIXME: ComponentBase should inherit from HTMLElement when custom elements API is available.
3 class ComponentBase {
4     constructor(name)
5     {
6         this._element = document.createElement(name);
7         this._element.component = (function () { return this; }).bind(this);
8         this._shadow = this._constructShadowTree();
9     }
10
11     element() { return this._element; }
12     content() { return this._shadow; }
13     render() { }
14
15     renderReplace(element, content) { ComponentBase.renderReplace(element, content); }
16
17     static renderReplace(element, content)
18     {
19         element.innerHTML = '';
20         if (content)
21             ComponentBase._addContentToElement(element, content);
22     }
23
24     _constructShadowTree()
25     {
26         var newTarget = this.__proto__.constructor;
27
28         var htmlTemplate = newTarget['htmlTemplate'];
29         var cssTemplate = newTarget['cssTemplate'];
30
31         if (!htmlTemplate && !cssTemplate)
32             return null;
33
34         var shadow = null;
35         if ('attachShadow' in Element.prototype)
36             shadow = this._element.attachShadow({mode: 'closed'});
37         else if ('createShadowRoot' in Element.prototype) // Legacy Chromium API.
38             shadow = this._element.createShadowRoot();
39         else
40             shadow = this._element;
41
42         if (htmlTemplate) {
43             var template = document.createElement('template');
44             template.innerHTML = newTarget.htmlTemplate();
45             shadow.appendChild(template.content.cloneNode(true));
46             this._recursivelyReplaceUnknownElementsByComponents(shadow);
47         }
48
49         if (cssTemplate) {
50             var style = document.createElement('style');
51             style.textContent = newTarget.cssTemplate();
52             shadow.appendChild(style);
53         }
54
55         return shadow;
56     }
57
58     _recursivelyReplaceUnknownElementsByComponents(parent)
59     {
60         if (!ComponentBase._map)
61             return;
62
63         var nextSibling;
64         for (var child = parent.firstChild; child; child = child.nextSibling) {
65             if (child instanceof HTMLUnknownElement || child instanceof HTMLElement) {
66                 var elementInterface = ComponentBase._map[child.localName];
67                 if (elementInterface) {
68                     var component = new elementInterface();
69                     var newChild = component.element();
70                     parent.replaceChild(newChild, child);
71                     child = newChild;
72                 }
73             }
74             this._recursivelyReplaceUnknownElementsByComponents(child);
75         }
76     }
77
78     static defineElement(name, elementInterface)
79     {
80         if (!ComponentBase._map)
81             ComponentBase._map = {};
82         ComponentBase._map[name] = elementInterface;
83     }
84
85     static createElement(name, attributes, content)
86     {
87         var element = document.createElement(name);
88         if (!content && (attributes instanceof Array || attributes instanceof Node
89             || attributes instanceof ComponentBase || typeof(attributes) != 'object')) {
90             content = attributes;
91             attributes = {};
92         }
93
94         if (attributes) {
95             for (var name in attributes) {
96                 if (name.startsWith('on'))
97                     element.addEventListener(name.substring(2), attributes[name]);
98                 else
99                     element.setAttribute(name, attributes[name]);
100             }
101         }
102
103         if (content)
104             ComponentBase._addContentToElement(element, content);
105
106         return element;
107     }
108
109     static _addContentToElement(element, content)
110     {
111         if (content instanceof Array) {
112             for (var nestedChild of content)
113                 this._addContentToElement(element, nestedChild);
114         } else if (content instanceof Node)
115             element.appendChild(content);
116          else if (content instanceof ComponentBase)
117             element.appendChild(content.element());
118         else
119             element.appendChild(document.createTextNode(content));
120     }
121
122     static createLink(content, titleOrCallback, callback, isExternal)
123     {
124         var title = titleOrCallback;
125         if (callback === undefined) {
126             title = content;
127             callback = titleOrCallback;
128         }
129
130         var attributes = {
131             href: '#',
132             title: title,
133         };
134
135         if (typeof(callback) === 'string')
136             attributes['href'] = callback;
137         else
138             attributes['onclick'] = ComponentBase.createActionHandler(callback);
139
140         if (isExternal)
141             attributes['target'] = '_blank';
142         return ComponentBase.createElement('a', attributes, content);
143     }
144
145     static createActionHandler(callback)
146     {
147         return function (event) {
148             event.preventDefault();
149             event.stopPropagation();
150             callback.call(this, event);
151         };
152     }
153 }
154
155 ComponentBase.css = Symbol();
156 ComponentBase.html = Symbol();
157 ComponentBase.map = {};