Instrument calls to render()
[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     updateRendering()
16     {
17         Instrumentation.startMeasuringTime('ComponentBase', 'updateRendering');
18         this.render();
19         Instrumentation.endMeasuringTime('ComponentBase', 'updateRendering');
20     }
21
22     renderReplace(element, content) { ComponentBase.renderReplace(element, content); }
23
24     static renderReplace(element, content)
25     {
26         element.innerHTML = '';
27         if (content)
28             ComponentBase._addContentToElement(element, content);
29     }
30
31     _constructShadowTree()
32     {
33         var newTarget = this.__proto__.constructor;
34
35         var htmlTemplate = newTarget['htmlTemplate'];
36         var cssTemplate = newTarget['cssTemplate'];
37
38         if (!htmlTemplate && !cssTemplate)
39             return null;
40
41         var shadow = null;
42         if ('attachShadow' in Element.prototype)
43             shadow = this._element.attachShadow({mode: 'closed'});
44         else if ('createShadowRoot' in Element.prototype) // Legacy Chromium API.
45             shadow = this._element.createShadowRoot();
46         else
47             shadow = this._element;
48
49         if (htmlTemplate) {
50             var template = document.createElement('template');
51             template.innerHTML = newTarget.htmlTemplate();
52             shadow.appendChild(template.content.cloneNode(true));
53             this._recursivelyReplaceUnknownElementsByComponents(shadow);
54         }
55
56         if (cssTemplate) {
57             var style = document.createElement('style');
58             style.textContent = newTarget.cssTemplate();
59             shadow.appendChild(style);
60         }
61
62         return shadow;
63     }
64
65     _recursivelyReplaceUnknownElementsByComponents(parent)
66     {
67         if (!ComponentBase._map)
68             return;
69
70         var nextSibling;
71         for (var child = parent.firstChild; child; child = child.nextSibling) {
72             if (child instanceof HTMLUnknownElement || child instanceof HTMLElement) {
73                 var elementInterface = ComponentBase._map[child.localName];
74                 if (elementInterface) {
75                     var component = new elementInterface();
76                     var newChild = component.element();
77                     parent.replaceChild(newChild, child);
78                     child = newChild;
79                 }
80             }
81             this._recursivelyReplaceUnknownElementsByComponents(child);
82         }
83     }
84
85     static defineElement(name, elementInterface)
86     {
87         if (!ComponentBase._map)
88             ComponentBase._map = {};
89         ComponentBase._map[name] = elementInterface;
90     }
91
92     static createElement(name, attributes, content)
93     {
94         var element = document.createElement(name);
95         if (!content && (attributes instanceof Array || attributes instanceof Node
96             || attributes instanceof ComponentBase || typeof(attributes) != 'object')) {
97             content = attributes;
98             attributes = {};
99         }
100
101         if (attributes) {
102             for (var name in attributes) {
103                 if (name.startsWith('on'))
104                     element.addEventListener(name.substring(2), attributes[name]);
105                 else
106                     element.setAttribute(name, attributes[name]);
107             }
108         }
109
110         if (content)
111             ComponentBase._addContentToElement(element, content);
112
113         return element;
114     }
115
116     static _addContentToElement(element, content)
117     {
118         if (content instanceof Array) {
119             for (var nestedChild of content)
120                 this._addContentToElement(element, nestedChild);
121         } else if (content instanceof Node)
122             element.appendChild(content);
123          else if (content instanceof ComponentBase)
124             element.appendChild(content.element());
125         else
126             element.appendChild(document.createTextNode(content));
127     }
128
129     static createLink(content, titleOrCallback, callback, isExternal)
130     {
131         var title = titleOrCallback;
132         if (callback === undefined) {
133             title = content;
134             callback = titleOrCallback;
135         }
136
137         var attributes = {
138             href: '#',
139             title: title,
140         };
141
142         if (typeof(callback) === 'string')
143             attributes['href'] = callback;
144         else
145             attributes['onclick'] = ComponentBase.createActionHandler(callback);
146
147         if (isExternal)
148             attributes['target'] = '_blank';
149         return ComponentBase.createElement('a', attributes, content);
150     }
151
152     static createActionHandler(callback)
153     {
154         return function (event) {
155             event.preventDefault();
156             event.stopPropagation();
157             callback.call(this, event);
158         };
159     }
160 }
161
162 ComponentBase.css = Symbol();
163 ComponentBase.html = Symbol();
164 ComponentBase.map = {};