Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / trace_viewer / cc / picture_debugger.html
1 <!DOCTYPE html>
2 <!--
3 Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
6 -->
7
8 <link rel="import" href="/cc/picture.html">
9 <link rel="import" href="/cc/picture_ops_chart_summary_view.html">
10 <link rel="import" href="/cc/picture_ops_chart_view.html">
11 <link rel="import" href="/cc/picture_ops_list_view.html">
12 <link rel="import" href="/tracing/analysis/generic_object_view.html">
13 <link rel="import" href="/tvcm/key_event_manager.html">
14 <link rel="import" href="/tvcm/ui/drag_handle.html">
15 <link rel="import" href="/tvcm/ui/info_bar.html">
16 <link rel="import" href="/tvcm/ui/list_view.html">
17 <link rel="import" href="/tvcm/ui/mouse_mode_selector.html">
18 <link rel="import" href="/tvcm/ui/overlay.html">
19 <link rel="import" href="/tvcm/utils.html">
20
21 <template id="picture-debugger-template">
22   <style>
23   picture-debugger {
24     -webkit-flex: 1 1 auto;
25     -webkit-flex-direction: row;
26     display: -webkit-flex;
27   }
28
29   picture-debugger > x-generic-object-view {
30     -webkit-flex-direction: column;
31     display: -webkit-flex;
32     width: 400px;
33   }
34
35   picture-debugger > left-panel {
36     -webkit-flex-direction: column;
37     display: -webkit-flex;
38     min-width: 300px;
39   }
40
41   picture-debugger > left-panel > picture-info {
42     -webkit-flex: 0 0 auto;
43     padding-top: 2px;
44   }
45
46   picture-debugger > left-panel > picture-info .title {
47     font-weight: bold;
48     margin-left: 5px;
49     margin-right: 5px;
50   }
51
52   picture-debugger > x-drag-handle {
53     -webkit-flex: 0 0 auto;
54   }
55
56   picture-debugger .filename {
57     -webkit-user-select: text;
58     margin-left: 5px;
59   }
60
61   picture-debugger > right-panel {
62     -webkit-flex: 1 1 auto;
63     -webkit-flex-direction: column;
64     display: -webkit-flex;
65   }
66
67   picture-debugger > right-panel > picture-ops-chart-view {
68     min-height: 150px;
69     min-width : 0;
70     overflow-x: auto;
71     overflow-y: hidden;
72   }
73
74   /******************************************************************************/
75
76   raster-area {
77     background-color: #ddd;
78     min-height: 200px;
79     min-width: 200px;
80     overflow-y: auto;
81     padding-left: 5px;
82   }
83   </style>
84
85   <left-panel>
86     <picture-info>
87       <div>
88         <span class='title'>Skia Picture</span>
89         <span class='size'></span>
90       </div>
91       <div>
92         <input class='filename' type='text' value='skpicture.skp' />
93         <button class='export'>Export</button>
94       </div>
95     </picture-info>
96   </left-panel>
97   <right-panel>
98     <picture-ops-chart-view></picture-ops-chart-view>
99     <raster-area><canvas></canvas></raster-area>
100   </right-panel>
101 </template>
102
103 <script>
104 'use strict';
105
106 tvcm.exportTo('cc', function() {
107   var THIS_DOC = document.currentScript.ownerDocument;
108
109   /**
110    * PictureDebugger is a view of a PictureSnapshot for inspecting
111    * the picture in detail. (e.g., timing information, etc.)
112    *
113    * @constructor
114    */
115   var PictureDebugger = tvcm.ui.define('picture-debugger');
116
117   PictureDebugger.prototype = {
118     __proto__: HTMLUnknownElement.prototype,
119
120     decorate: function() {
121       var node = tvcm.instantiateTemplate('#picture-debugger-template',
122           THIS_DOC);
123
124       this.appendChild(node);
125
126       this.pictureAsImageData_ = undefined;
127       this.showOverdraw_ = false;
128       this.zoomScaleValue_ = 1;
129
130       this.sizeInfo_ = this.querySelector('.size');
131       this.rasterArea_ = this.querySelector('raster-area');
132       this.rasterCanvas_ = this.rasterArea_.querySelector('canvas');
133       this.rasterCtx_ = this.rasterCanvas_.getContext('2d');
134
135       this.filename_ = this.querySelector('.filename');
136
137       this.drawOpsChartSummaryView_ = new cc.PictureOpsChartSummaryView();
138       this.drawOpsChartView_ = new cc.PictureOpsChartView();
139       this.drawOpsChartView_.addEventListener(
140           'selection-changed', this.onChartBarClicked_.bind(this));
141
142       this.exportButton_ = this.querySelector('.export');
143       this.exportButton_.addEventListener(
144           'click', this.onSaveAsSkPictureClicked_.bind(this));
145
146       this.trackMouse_();
147
148       var overdrawCheckbox = tvcm.ui.createCheckBox(
149           this, 'showOverdraw',
150           'pictureView.showOverdraw', false,
151           'Show overdraw');
152
153       var chartCheckbox = tvcm.ui.createCheckBox(
154           this, 'showSummaryChart',
155           'pictureView.showSummaryChart', false,
156           'Show timing summary');
157
158       var pictureInfo = this.querySelector('picture-info');
159       pictureInfo.appendChild(overdrawCheckbox);
160       pictureInfo.appendChild(chartCheckbox);
161
162       this.drawOpsView_ = new cc.PictureOpsListView();
163       this.drawOpsView_.addEventListener(
164           'selection-changed', this.onChangeDrawOps_.bind(this));
165
166       var leftPanel = this.querySelector('left-panel');
167       leftPanel.appendChild(this.drawOpsChartSummaryView_);
168       leftPanel.appendChild(this.drawOpsView_);
169
170       var middleDragHandle = new tvcm.ui.DragHandle();
171       middleDragHandle.horizontal = false;
172       middleDragHandle.target = leftPanel;
173
174       var rightPanel = this.querySelector('right-panel');
175       rightPanel.replaceChild(
176           this.drawOpsChartView_,
177           rightPanel.querySelector('picture-ops-chart-view'));
178
179       this.infoBar_ = new tvcm.ui.InfoBar();
180       this.rasterArea_.appendChild(this.infoBar_);
181
182       this.insertBefore(middleDragHandle, rightPanel);
183
184       this.picture_ = undefined;
185
186       tvcm.KeyEventManager.instance.addListener(
187           'keypress', this.onKeyPress_, this);
188
189       // Add a mutation observer so that when the view is resized we can
190       // update the chart summary view.
191       this.mutationObserver_ = new MutationObserver(
192           this.onMutation_.bind(this));
193       this.mutationObserver_.observe(leftPanel, { attributes: true });
194     },
195
196     onKeyPress_: function(e) {
197       if (e.keyCode == 'h'.charCodeAt(0)) {
198         this.moveSelectedOpBy(-1);
199         e.preventDefault();
200         e.stopPropagation();
201       } else if (e.keyCode == 'l'.charCodeAt(0)) {
202         this.moveSelectedOpBy(1);
203         e.preventDefault();
204         e.stopPropagation();
205       }
206     },
207
208     onMutation_: function(mutations) {
209
210       for (var m = 0; m < mutations.length; m++) {
211         // A style change would indicate that the element has resized
212         // so we should re-render the chart.
213         if (mutations[m].attributeName === 'style') {
214           this.drawOpsChartSummaryView_.requiresRedraw = true;
215           this.drawOpsChartSummaryView_.updateChartContents();
216
217           this.drawOpsChartView_.dimensionsHaveChanged = true;
218           this.drawOpsChartView_.updateChartContents();
219           break;
220         }
221       }
222     },
223
224     onSaveAsSkPictureClicked_: function() {
225       // Decode base64 data into a String
226       var rawData = atob(this.picture_.getBase64SkpData());
227
228       // Convert this String into an Uint8Array
229       var length = rawData.length;
230       var arrayBuffer = new ArrayBuffer(length);
231       var uint8Array = new Uint8Array(arrayBuffer);
232       for (var c = 0; c < length; c++)
233         uint8Array[c] = rawData.charCodeAt(c);
234
235       // Create a blob URL from the binary array.
236       var blob = new Blob([uint8Array], {type: 'application/octet-binary'});
237       var blobUrl = window.webkitURL.createObjectURL(blob);
238
239       // Create a link and click on it. BEST API EVAR!
240       var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
241       link.href = blobUrl;
242       link.download = this.filename_.value;
243       var event = document.createEvent('MouseEvents');
244       event.initMouseEvent(
245           'click', true, false, window, 0, 0, 0, 0, 0,
246           false, false, false, false, 0, null);
247       link.dispatchEvent(event);
248     },
249
250     get picture() {
251       return this.picture_;
252     },
253
254     set picture(picture) {
255       this.drawOpsView_.picture = picture;
256       this.drawOpsChartView_.picture = picture;
257       this.drawOpsChartSummaryView_.picture = picture;
258       this.picture_ = picture;
259
260       this.exportButton_.disabled = !this.picture_.canSave;
261
262       if (picture) {
263         var size = this.getRasterCanvasSize_();
264         this.rasterCanvas_.width = size.width;
265         this.rasterCanvas_.height = size.height;
266       }
267
268       var bounds = this.rasterArea_.getBoundingClientRect();
269       var selectorBounds = this.mouseModeSelector_.getBoundingClientRect();
270       this.mouseModeSelector_.pos = {
271         x: (bounds.right - selectorBounds.width - 10),
272         y: bounds.top
273       };
274
275       this.rasterize_();
276
277       this.scheduleUpdateContents_();
278     },
279
280     getRasterCanvasSize_: function() {
281       var style = window.getComputedStyle(this.rasterArea_);
282       var width =
283           Math.max(parseInt(style.width), this.picture_.layerRect.width);
284       var height =
285           Math.max(parseInt(style.height), this.picture_.layerRect.height);
286
287       return {
288         width: width,
289         height: height
290       };
291     },
292
293     scheduleUpdateContents_: function() {
294       if (this.updateContentsPending_)
295         return;
296       this.updateContentsPending_ = true;
297       tvcm.requestAnimationFrameInThisFrameIfPossible(
298           this.updateContents_.bind(this)
299       );
300     },
301
302     updateContents_: function() {
303       this.updateContentsPending_ = false;
304
305       if (this.picture_) {
306         this.sizeInfo_.textContent = '(' +
307             this.picture_.layerRect.width + ' x ' +
308             this.picture_.layerRect.height + ')';
309       }
310
311       this.drawOpsChartView_.updateChartContents();
312       this.drawOpsChartView_.scrollSelectedItemIntoViewIfNecessary();
313
314       // Return if picture hasn't finished rasterizing.
315       if (!this.pictureAsImageData_)
316         return;
317
318       this.infoBar_.visible = false;
319       this.infoBar_.removeAllButtons();
320       if (this.pictureAsImageData_.error) {
321         this.infoBar_.message = 'Cannot rasterize...';
322         this.infoBar_.addButton('More info...', function(e) {
323           var overlay = new tvcm.ui.Overlay();
324           overlay.textContent = this.pictureAsImageData_.error;
325           overlay.visible = true;
326           e.stopPropagation();
327           return false;
328         }.bind(this));
329         this.infoBar_.visible = true;
330       }
331
332       this.drawPicture_();
333     },
334
335     drawPicture_: function() {
336       var size = this.getRasterCanvasSize_();
337       if (size.width !== this.rasterCanvas_.width)
338         this.rasterCanvas_.width = size.width;
339       if (size.height !== this.rasterCanvas_.height)
340         this.rasterCanvas_.heigth = size.height;
341
342       this.rasterCtx_.clearRect(0, 0, size.width, size.height);
343
344       if (!this.pictureAsImageData_.imageData)
345         return;
346
347       var imgCanvas = this.pictureAsImageData_.asCanvas();
348       var w = imgCanvas.width;
349       var h = imgCanvas.height;
350       this.rasterCtx_.drawImage(imgCanvas, 0, 0, w, h,
351                                 0, 0, w * this.zoomScaleValue_,
352                                 h * this.zoomScaleValue_);
353     },
354
355     rasterize_: function() {
356       if (this.picture_) {
357         this.picture_.rasterize(
358             {
359               stopIndex: this.drawOpsView_.selectedOpIndex,
360               showOverdraw: this.showOverdraw_
361             },
362             this.onRasterComplete_.bind(this));
363       }
364     },
365
366     onRasterComplete_: function(pictureAsImageData) {
367       this.pictureAsImageData_ = pictureAsImageData;
368       this.scheduleUpdateContents_();
369     },
370
371     moveSelectedOpBy: function(increment) {
372       if (this.selectedOpIndex === undefined) {
373         this.selectedOpIndex = 0;
374         return;
375       }
376       this.selectedOpIndex = tvcm.clamp(
377           this.selectedOpIndex + increment,
378           0, this.numOps);
379     },
380
381     get numOps() {
382       return this.drawOpsView_.numOps;
383     },
384
385     get selectedOpIndex() {
386       return this.drawOpsView_.selectedOpIndex;
387     },
388
389     set selectedOpIndex(index) {
390       this.drawOpsView_.selectedOpIndex = index;
391       this.drawOpsChartView_.selectedOpIndex = index;
392     },
393
394     onChartBarClicked_: function(e) {
395       this.drawOpsView_.selectedOpIndex =
396           this.drawOpsChartView_.selectedOpIndex;
397     },
398
399     onChangeDrawOps_: function(e) {
400       this.rasterize_();
401       this.scheduleUpdateContents_();
402
403       this.drawOpsChartView_.selectedOpIndex =
404           this.drawOpsView_.selectedOpIndex;
405     },
406
407     set showOverdraw(v) {
408       this.showOverdraw_ = v;
409       this.rasterize_();
410     },
411
412     set showSummaryChart(chartShouldBeVisible) {
413       if (chartShouldBeVisible)
414         this.drawOpsChartSummaryView_.show();
415       else
416         this.drawOpsChartSummaryView_.hide();
417     },
418
419     trackMouse_: function() {
420       this.mouseModeSelector_ = new tvcm.ui.MouseModeSelector(this.rasterArea_);
421       this.rasterArea_.appendChild(this.mouseModeSelector_);
422
423       this.mouseModeSelector_.supportedModeMask =
424           tvcm.ui.MOUSE_SELECTOR_MODE.ZOOM;
425       this.mouseModeSelector_.mode = tvcm.ui.MOUSE_SELECTOR_MODE.ZOOM;
426       this.mouseModeSelector_.defaultMode = tvcm.ui.MOUSE_SELECTOR_MODE.ZOOM;
427       this.mouseModeSelector_.settingsKey = 'pictureDebugger.mouseModeSelector';
428
429       this.mouseModeSelector_.addEventListener('beginzoom',
430           this.onBeginZoom_.bind(this));
431       this.mouseModeSelector_.addEventListener('updatezoom',
432           this.onUpdateZoom_.bind(this));
433       this.mouseModeSelector_.addEventListener('endzoom',
434           this.onEndZoom_.bind(this));
435     },
436
437     onBeginZoom_: function(e) {
438       this.isZooming_ = true;
439
440       this.lastMouseViewPos_ = this.extractRelativeMousePosition_(e);
441
442       e.preventDefault();
443     },
444
445     onUpdateZoom_: function(e) {
446       if (!this.isZooming_)
447         return;
448
449       var currentMouseViewPos = this.extractRelativeMousePosition_(e);
450
451       // Take the distance the mouse has moved and we want to zoom at about
452       // 1/1000th of that speed. 0.01 feels jumpy. This could possibly be tuned
453       // more if people feel it's too slow.
454       this.zoomScaleValue_ +=
455           ((this.lastMouseViewPos_.y - currentMouseViewPos.y) * 0.001);
456       this.zoomScaleValue_ = Math.max(this.zoomScaleValue_, 0.1);
457
458       this.drawPicture_();
459
460       this.lastMouseViewPos_ = currentMouseViewPos;
461     },
462
463     onEndZoom_: function(e) {
464       this.lastMouseViewPos_ = undefined;
465       this.isZooming_ = false;
466       e.preventDefault();
467     },
468
469     extractRelativeMousePosition_: function(e) {
470       return {
471         x: e.clientX - this.rasterArea_.offsetLeft,
472         y: e.clientY - this.rasterArea_.offsetTop
473       };
474     }
475   };
476
477   return {
478     PictureDebugger: PictureDebugger
479   };
480 });
481 </script>