Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 <!DOCTYPE html> | |
| 2 <!-- | |
| 3 Copyright 2016 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="/tracing/ui/base/table.html"> | |
| 9 <link rel="import" href="/tracing/ui/side_panel/side_panel.html"> | |
| 10 <link rel="import" href="/tracing/value/ui/scalar_span.html"> | |
| 11 <link rel="import" href="/tracing/value/unit.html"> | |
| 12 | |
| 13 <polymer-element name='tr-ui-e-s-frame-data-side-panel' | |
| 14 extends='tr-ui-side-panel'> | |
| 15 <template> | |
| 16 <style> | |
| 17 :host { | |
| 18 display: flex; | |
| 19 width: 600px; | |
| 20 flex-direction: column; | |
| 21 } | |
| 22 table-container { | |
| 23 display: flex; | |
| 24 overflow: auto; | |
| 25 } | |
| 26 </style> | |
| 27 <table-container> | |
| 28 <tr-ui-b-table id="table"></tr-ui-b-table> | |
| 29 </table-container> | |
| 30 </template> | |
| 31 </polymer-element> | |
| 32 | |
| 33 <script> | |
| 34 'use strict'; | |
| 35 tr.exportTo('tr.ui.e.s', function() { | |
| 36 | |
| 37 /** | |
| 38 * @constructor | |
| 39 */ | |
| 40 function Row(context) { | |
| 41 this.type = context.objectInstance.blameContextType; | |
| 42 this.contexts = [context]; | |
| 43 if (context.crossProcessCounterpart) | |
| 44 this.contexts.push(context.crossProcessCounterpart); | |
| 45 | |
| 46 // TODO(xiaochengh): Handle the case where a subframe has a trivial url | |
| 47 // (e.g., about:blank), but inherits the origin of its parent. This is not | |
| 48 // needed now, but will be required if we want to group rows by origin. | |
| 49 this.url = context.url; | |
| 50 | |
| 51 this.renderer = undefined; | |
| 52 this.contexts.some(function(context) { | |
| 53 if (context.objectInstance.isTracedByRenderer) { | |
| 54 this.renderer = context.objectInstance.pid; | |
| 55 return true; | |
| 56 } | |
| 57 }, this); | |
| 58 | |
| 59 // To be computed in batch later for efficiency. | |
| 60 this.eventsOfInterest = new tr.model.EventSet(this.contexts); | |
| 61 this.time = 0; | |
| 62 } | |
| 63 | |
| 64 Polymer('tr-ui-e-s-frame-data-side-panel', { | |
| 65 ready: function() { | |
| 66 this.model_ = undefined; | |
| 67 this.rangeOfInterest_ = new tr.b.Range(); | |
| 68 | |
| 69 // TODO(xiaochengh): Design proper grouping of the rows (by renderer | |
| 70 // pid, frame tree topology, site, ...) in a follow-up patch. | |
| 71 this.$.table.showHeader = true; | |
| 72 this.$.table.selectionMode = tr.ui.b.TableFormat.SelectionMode.ROW; | |
| 73 this.$.table.tableColumns = this.createFrameDataTableColumns_(); | |
| 74 | |
| 75 this.$.table.addEventListener('selection-changed', function(e) { | |
| 76 this.selectEventSet_(this.$.table.selectedTableRow.eventsOfInterest); | |
| 77 }.bind(this)); | |
| 78 }, | |
| 79 | |
| 80 selectEventSet_: function(eventSet) { | |
| 81 var event = new tr.model.RequestSelectionChangeEvent(); | |
| 82 event.selection = eventSet; | |
| 83 this.dispatchEvent(event); | |
| 84 }, | |
| 85 | |
| 86 createFrameDataTableColumns_: function() { | |
| 87 return [ | |
| 88 { | |
| 89 title: 'Renderer', | |
| 90 value: row => row.renderer, | |
| 91 cmp: (a, b) => a.renderer - b.renderer | |
| 92 }, | |
| 93 { | |
| 94 title: 'Type', | |
| 95 value: row => row.type | |
| 96 }, | |
| 97 // TODO(xiaochengh): Decide what details to show in the table: | |
| 98 // - URL seems necessary, but we may also want origin instead/both. | |
| 99 // - Distinguish between browser time and renderer time? | |
| 100 // - Distinguish between CPU time and wall clock time? | |
| 101 // - Memory? Network? ... | |
| 102 { | |
| 103 title: 'Time', | |
| 104 value: row => tr.v.ui.createScalarSpan(row.time, { | |
| 105 unit: tr.v.Unit.byName.timeStampInMs, | |
|
petrcermak
2016/05/31 09:57:24
nit: we generally indent 2 spaces inside object li
Xiaocheng
2016/05/31 12:01:10
Done.
| |
| 106 ownerDocument: this.ownerDocument | |
| 107 } | |
| 108 ), | |
| 109 cmp: (a, b) => a.time - b.time | |
| 110 }, | |
| 111 { | |
| 112 title: 'URL', | |
| 113 value: row => row.url, | |
| 114 cmp: (a, b) => (a.url || '').localeCompare(b.url) | |
|
petrcermak
2016/05/31 09:57:24
|''.localeCompared(undefined)| returns -1 while |'
Xiaocheng
2016/05/31 12:01:11
Done.
| |
| 115 } | |
| 116 ]; | |
| 117 }, | |
| 118 | |
| 119 createFrameDataTableRows_: function() { | |
| 120 if (!this.model_) | |
| 121 return []; | |
| 122 | |
| 123 // Gather contexts into skeletons of rows. | |
| 124 var rows = []; | |
| 125 var rowMap = {}; | |
| 126 tr.b.iterItems(this.model_.processes, function(pid, process) { | |
| 127 process.objects.iterObjectInstances(function(objectInstance) { | |
| 128 if (!objectInstance.blameContextType) | |
| 129 return; | |
| 130 objectInstance.snapshots.forEach(function(snapshot) { | |
| 131 if (rowMap[snapshot.guid]) | |
| 132 return; | |
| 133 var row = new Row(snapshot); | |
| 134 row.contexts.forEach(context => rowMap[context.guid] = row); | |
| 135 rows.push(row); | |
| 136 }, this); | |
| 137 }, this); | |
| 138 }, this); | |
| 139 | |
| 140 // Find slices attributed to each row. | |
| 141 tr.b.iterItems(this.model_.processes, function(pid, process) { | |
| 142 tr.b.iterItems(process.threads, function(tid, thread) { | |
| 143 // TODO(xiaochengh): Is it possible for AsyncSlices to be attributed? | |
| 144 // Currently they are just ignored. | |
| 145 thread.sliceGroup.iterSlicesInTimeRange(function(topLevelSlice) { | |
| 146 topLevelSlice.contexts.forEach(function(context) { | |
| 147 if (!context.snapshot.guid || !rowMap[context.snapshot.guid]) { | |
| 148 // TODO(xiaochengh): Throw some warning for broken reference. | |
| 149 return; | |
| 150 } | |
| 151 var row = rowMap[context.snapshot.guid]; | |
| 152 row.eventsOfInterest.push(topLevelSlice); | |
| 153 row.time += topLevelSlice.selfTime || 0; | |
| 154 }); | |
| 155 }, this.currentRangeOfInterest.min, this.currentRangeOfInterest.max); | |
| 156 }, this); | |
| 157 }, this); | |
| 158 | |
| 159 return rows; | |
| 160 }, | |
| 161 updateContents_: function() { | |
| 162 this.$.table.tableRows = this.createFrameDataTableRows_(); | |
| 163 this.$.table.rebuild(); | |
| 164 }, | |
| 165 | |
| 166 supportsModel: function(m) { | |
| 167 if (!m) { | |
| 168 return { | |
| 169 supported: false, | |
| 170 reason: 'No model available.' | |
| 171 }; | |
| 172 } | |
| 173 | |
| 174 var ans = {supported: false}; | |
| 175 tr.b.iterItems(m.processes, function(pid, process) { | |
| 176 process.objects.iterObjectInstances(function(instance) { | |
| 177 if (instance.blameContextType) | |
| 178 ans.supported = true; | |
| 179 }); | |
| 180 }, this); | |
| 181 | |
| 182 if (!ans.supported) | |
| 183 ans.reason = 'No frame data available'; | |
| 184 return ans; | |
| 185 }, | |
| 186 | |
| 187 get currentRangeOfInterest() { | |
| 188 if (this.rangeOfInterest_.isEmpty) | |
| 189 return this.model_.bounds; | |
| 190 else | |
| 191 return this.rangeOfInterest_; | |
| 192 }, | |
| 193 | |
| 194 get rangeOfInterest() { | |
| 195 return this.rangeOfInterest_; | |
| 196 }, | |
| 197 | |
| 198 set rangeOfInterest(rangeOfInterest) { | |
| 199 this.rangeOfInterest_ = rangeOfInterest; | |
| 200 this.updateContents_(); | |
| 201 }, | |
| 202 | |
| 203 get selection() { | |
| 204 // Not applicable. | |
| 205 }, | |
| 206 | |
| 207 set selection(_) { | |
| 208 // Not applicable. | |
| 209 }, | |
| 210 | |
| 211 get textLabel() { | |
| 212 return 'Frame Data'; | |
| 213 }, | |
| 214 | |
| 215 get model() { | |
| 216 return this.model_; | |
| 217 }, | |
| 218 | |
| 219 set model(model) { | |
| 220 this.model_ = model; | |
| 221 this.updateContents_(); | |
| 222 } | |
| 223 }); | |
| 224 }); | |
| 225 </script> | |
| OLD | NEW |