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.
8 <link rel="stylesheet" href="/cc/picture_ops_list_view.css">
10 <link rel="import" href="/cc/constants.html">
11 <link rel="import" href="/cc/selection.html">
12 <link rel="import" href="/tvcm/ui/list_view.html">
13 <link rel="import" href="/tvcm/ui/dom_helpers.html">
18 tvcm.exportTo('cc', function() {
19 var OPS_TIMING_ITERATIONS = 3; // Iterations to average op timing info over.
20 var ANNOTATION = 'Comment';
21 var BEGIN_ANNOTATION = 'BeginCommentGroup';
22 var END_ANNOTATION = 'EndCommentGroup';
23 var ANNOTATION_ID = 'ID: ';
24 var ANNOTATION_CLASS = 'CLASS: ';
25 var ANNOTATION_TAG = 'TAG: ';
27 var constants = cc.constants;
32 var PictureOpsListView = tvcm.ui.define('picture-ops-list-view');
34 PictureOpsListView.prototype = {
35 __proto__: HTMLUnknownElement.prototype,
37 decorate: function() {
38 this.opsList_ = new tvcm.ui.ListView();
39 this.appendChild(this.opsList_);
41 this.selectedOp_ = undefined;
42 this.selectedOpIndex_ = undefined;
43 this.opsList_.addEventListener(
44 'selection-changed', this.onSelectionChanged_.bind(this));
46 this.picture_ = undefined;
53 set picture(picture) {
54 this.picture_ = picture;
55 this.updateContents_();
58 updateContents_: function() {
59 this.opsList_.clear();
64 var ops = this.picture_.getOps();
68 ops = this.picture_.tagOpsWithTimings(ops);
70 ops = this.opsTaggedWithAnnotations_(ops);
72 for (var i = 0; i < ops.length; i++) {
74 var item = document.createElement('div');
75 item.opIndex = op.opIndex;
76 item.textContent = i + ') ' + op.cmd_string;
78 // Display the element info associated with the op, if available.
79 if (op.elementInfo.tag || op.elementInfo.id || op.elementInfo.class) {
80 var elementInfo = document.createElement('span');
81 elementInfo.classList.add('elementInfo');
82 var tag = op.elementInfo.tag ? op.elementInfo.tag : 'unknown';
83 var id = op.elementInfo.id ? 'id=' + op.elementInfo.id : undefined;
84 var className = op.elementInfo.class ? 'class=' +
85 op.elementInfo.class : undefined;
86 elementInfo.textContent =
87 '<' + tag + (id ? ' ' : '') +
88 (id ? id : '') + (className ? ' ' : '') +
89 (className ? className : '') + '>';
90 item.appendChild(elementInfo);
93 // Display each of the Skia ops.
94 op.info.forEach(function(info) {
95 var infoItem = document.createElement('div');
96 infoItem.textContent = info;
97 item.appendChild(infoItem);
100 // Display the op timing, if available.
101 if (op.cmd_time && op.cmd_time >= 0.0001) {
102 var time = document.createElement('span');
103 time.classList.add('time');
104 var rounded = op.cmd_time.toFixed(4);
105 time.textContent = '(' + rounded + 'ms)';
106 item.appendChild(time);
109 this.opsList_.appendChild(item);
113 onSelectionChanged_: function(e) {
114 var beforeSelectedOp = true;
116 // Deselect on re-selection.
117 if (this.opsList_.selectedElement === this.selectedOp_) {
118 this.opsList_.selectedElement = undefined;
119 beforeSelectedOp = false;
120 this.selectedOpIndex_ = undefined;
123 this.selectedOp_ = this.opsList_.selectedElement;
125 // Set selection on all previous ops.
126 var ops = this.opsList_.children;
127 for (var i = 0; i < ops.length; i++) {
129 if (op === this.selectedOp_) {
130 beforeSelectedOp = false;
131 this.selectedOpIndex_ = op.opIndex;
132 } else if (beforeSelectedOp) {
133 op.setAttribute('beforeSelection', 'beforeSelection');
135 op.removeAttribute('beforeSelection');
139 tvcm.dispatchSimpleEvent(this, 'selection-changed', false);
143 return this.opsList_.children.length;
146 get selectedOpIndex() {
147 return this.selectedOpIndex_;
150 set selectedOpIndex(s) {
151 this.selectedOpIndex_ = s;
153 if (s === undefined) {
154 this.opsList_.selectedElement = this.selectedOp_;
155 this.onSelectionChanged_();
157 if (s < 0) throw new Error('Invalid index');
158 if (s >= this.numOps) throw new Error('Invalid index');
159 this.opsList_.selectedElement = this.opsList_.getElementByIndex(s + 1);
160 tvcm.scrollIntoViewIfNeeded(this.opsList_.selectedElement);
165 * Return Skia operations tagged by annotation.
167 * The ops returned from Picture.getOps() contain both Skia ops and
168 * annotations threaded together. This function removes all annotations
169 * from the list and tags each op with the associated annotations.
170 * Additionally, the last {tag, id, class} is stored as elementInfo on
173 * @param {Array} ops Array of Skia operations and annotations.
174 * @return {Array} Skia ops where op.annotations contains the associated
175 * annotations for a given op.
177 opsTaggedWithAnnotations_: function(ops) {
178 // This algorithm works by walking all the ops and pushing any
179 // annotations onto a stack. When a non-annotation op is found, the
180 // annotations stack is traversed and stored with the op.
181 var annotationGroups = new Array();
182 var opsWithoutAnnotations = new Array();
183 for (var opIndex = 0; opIndex < ops.length; opIndex++) {
184 var op = ops[opIndex];
185 op.opIndex = opIndex;
186 switch (op.cmd_string) {
187 case BEGIN_ANNOTATION:
188 annotationGroups.push(new Array());
191 annotationGroups.pop();
194 annotationGroups[annotationGroups.length - 1].push(op);
197 var annotations = new Array();
198 var elementInfo = {};
199 annotationGroups.forEach(function(annotationGroup) {
201 annotationGroup.forEach(function(annotation) {
202 annotation.info.forEach(function(info) {
203 if (info.indexOf(ANNOTATION_TAG) != -1)
204 elementInfo.tag = info.substring(
205 info.indexOf(ANNOTATION_TAG) +
206 ANNOTATION_TAG.length).toLowerCase();
207 else if (info.indexOf(ANNOTATION_ID) != -1)
208 elementInfo.id = info.substring(
209 info.indexOf(ANNOTATION_ID) +
210 ANNOTATION_ID.length);
211 else if (info.indexOf(ANNOTATION_CLASS) != -1)
212 elementInfo.class = info.substring(
213 info.indexOf(ANNOTATION_CLASS) +
214 ANNOTATION_CLASS.length);
216 annotations.push(info);
220 op.annotations = annotations;
221 op.elementInfo = elementInfo;
222 opsWithoutAnnotations.push(op);
226 return opsWithoutAnnotations;
231 PictureOpsListView: PictureOpsListView