1<!DOCTYPE html> 2<!-- 3Copyright (c) 2015 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7<link rel="import" href="/perf_insights/function_handle.html"> 8<link rel="import" href="/perf_insights/mre/job.html"> 9<link rel="import" href="/perf_insights/mre/mre_result.html"> 10<link rel="import" href="/perf_insights/ui/generic_results_view.html"> 11<link rel="import" href="/perf_insights/ui/reports/pi_report.html"> 12<link rel="import" href="/tracing/base/xhr.html"> 13<link rel="import" href="/tracing/ui/base/dom_helpers.html"> 14<link rel="import" href="/tracing/ui/base/info_bar_group.html"> 15<link rel="import" href="/tracing/ui/base/polymer_utils.html"> 16 17<polymer-element name="pi-ui-pi-app-main"> 18 <template> 19 <style> 20 :host { 21 display: flex; 22 flex-direction: column; 23 } 24 25 top-controls { 26 display: flex; 27 flex: 0 0 auto; 28 background-color: rgb(236, 236, 236); 29 border-bottom: 1px solid #8e8e8e; 30 padding: 4px; 31 } 32 33 top-controls > span.spacer { 34 flex: 1 1 auto; 35 } 36 37 top-left-controls { 38 display: flex; 39 } 40 top-right-controls { 41 display: flex; 42 } 43 44 report-container { 45 display: flex; 46 flex: 1 1 auto; 47 min-height: 0; 48 min-width: 0; 49 overflow: auto; 50 } 51 report-container > * { 52 flex: 1 1 auto; 53 } 54 </style> 55 <top-controls> 56 <top-left-controls id="top_left_controls"></top-left-controls> 57 <span class="spacer"></span> 58 <top-right-controls id="top_right_controls"></top-right-controls> 59 </top-controls> 60 <tr-ui-b-info-bar-group id="infobars"></tr-ui-b-info-bar-group> 61 <report-container id="report_container"></report-container> 62 </template> 63</polymer-element> 64<script> 65'use strict'; 66 67tr.exportTo('pi.ui', function() { 68 function areMappingStatesSame(a, b) { 69 for (var k in a) 70 if (a[k] !== b[k]) 71 return false; 72 return true; 73 } 74 75 Polymer('pi-ui-pi-app-main', { 76 created: function() { 77 // Non-mapping state. 78 this.mapTracesDrivers_ = undefined; 79 80 // Mapping state. 81 this.currentMapTracesDriver_ = undefined; 82 this.corpusQuery_ = undefined; 83 this.piReportElementName_ = undefined; 84 this.showRawResults_ = false; 85 }, 86 87 ready: function() { 88 var topLeftControls = this.$.top_left_controls; 89 var topRightControls = this.$.top_right_controls; 90 91 var piDriverPolymerElementNames = tr.ui.b.getPolymerElementsThatSubclass( 92 'pi-driver-base'); 93 var piDriverElementOptions = piDriverPolymerElementNames.map( 94 function(peTagName) { 95 var pe = tr.ui.b.getPolymerElementNamed(peTagName); 96 return { 97 label: pe.getAttribute('display-name'), 98 value: peTagName 99 }; 100 }); 101 102 var driverSelector = tr.ui.b.createSelector( 103 this, 'currentMapTracesDriver', 104 'pi.app_main.currentMapTracesDriver', 105 piDriverElementOptions[0].value, 106 piDriverElementOptions); 107 108 topLeftControls.appendChild(driverSelector); 109 110 var corpusSelections = [ 111 { 112 'label': 'Deep Reports', 113 'value': 'https://performance-insights.appspot.com' 114 }, 115 { 116 'label': 'Bulk Reports', 117 'value': 'https://performance-insights.appspot.com' 118 } 119 ]; 120 var corpusSelector = tr.ui.b.createSelector( 121 this, 'corpus', 122 'pi.app_main.corpus', 123 corpusSelections[0].value, 124 corpusSelections); 125 topLeftControls.appendChild(corpusSelector); 126 127 var corpusQuerySelector = tr.ui.b.createTextInput( 128 this, 'corpusQuery', 129 'pi.app_main.corpusQuery', 130 'MAX_TRACE_HANDLES=10'); 131 132 var self = this; 133 function onQuerySelectorKeypress(e) { 134 var key = e.which || e.keyCode; 135 if (key === 13) { // 13 is enter 136 self.scheduleUpdateContents_(); 137 self.fire('ui-state-changed'); 138 } 139 } 140 141 corpusQuerySelector.addEventListener('keypress', onQuerySelectorKeypress); 142 143 corpusQuerySelector.style.width = '350px'; 144 topLeftControls.appendChild(corpusQuerySelector); 145 146 var piReportPolymerElementNames = tr.ui.b.getPolymerElementsThatSubclass( 147 'pi-ui-r-pi-report'); 148 var piReportElementOptions = piReportPolymerElementNames.map( 149 function(peTagName) { 150 return { 151 label: peTagName, 152 value: peTagName 153 }; 154 }); 155 var reportSelector = tr.ui.b.createSelector( 156 this, 'piReportElementName', 157 'pi.app_main.piReportElementName', 158 piReportElementOptions[0].value, 159 piReportElementOptions); 160 topLeftControls.appendChild(reportSelector); 161 162 var self = this; 163 function onProcessButton() { 164 self.scheduleUpdateContents_(); 165 self.fire('ui-state-changed'); 166 } 167 168 var processButton = tr.ui.b.createButton( 169 this, 'processButton', 170 'Process!', onProcessButton); 171 topLeftControls.appendChild(processButton); 172 173 var showRawResultsCheckbox = tr.ui.b.createCheckBox( 174 this, 'showRawResults', 175 'pi.app_main.showRawResults', false, 176 'Show raw results'); 177 topRightControls.appendChild(showRawResultsCheckbox); 178 }, 179 180 get mapTracesDrivers() { 181 return this.mapTracesDrivers_; 182 }, 183 184 get mappingState() { 185 return { 186 currentMapTracesDriver: this.currentMapTracesDriver_, 187 corpusQuery: this.corpusQuery_, 188 piReportElementName: this.piReportElementName_, 189 showRawResults: this.showRawResults_ 190 }; 191 }, 192 193 get currentMapTracesDriver() { 194 return this.currentMapTracesDriver_; 195 }, 196 197 set currentMapTracesDriver(currentMapTracesDriver) { 198 this.currentMapTracesDriver_ = currentMapTracesDriver; 199 }, 200 201 get corpusQuery() { 202 return this.corpusQuery_; 203 }, 204 205 set corpusQuery(corpusQuery) { 206 this.corpusQuery_ = corpusQuery; 207 }, 208 209 get piReportElementName() { 210 return this.piReportElementName_; 211 }, 212 213 set piReportElementName(piReportElementName) { 214 this.piReportElementName_ = piReportElementName; 215 }, 216 217 get mapClientSide() { 218 return this.mapClientSide_; 219 }, 220 221 set mapClientSide(mapClientSide) { 222 this.mapClientSide_ = mapClientSide; 223 }, 224 225 get showRawResults() { 226 return this.showRawResults_; 227 }, 228 229 set showRawResults(showRawResults) { 230 this.showRawResults_ = showRawResults; 231 }, 232 233 scheduleUpdateContents_: function() { 234 if (this.pendingUpdateContentsPromise_) 235 return; 236 237 var mappingState = this.mappingState; 238 239 var p = this.beginUpdatingContents_(mappingState); 240 p = p.catch(function(err) { 241 tr.showPanic('Something is wrong', err); 242 }); 243 p = p.then(updateDone.bind(this)); 244 245 function updateDone() { 246 this.pendingUpdateContentsPromise_ = undefined; 247 if (!areMappingStatesSame(this.mappingState, mappingState)) 248 this.scheduleUpdateContents_(); 249 } 250 this.pendingUpdateContentsPromise_ = p; 251 }, 252 253 beginUpdatingContents_: function(mappingState) { 254 var pe = tr.ui.b.getPolymerElementNamed(mappingState.piReportElementName); 255 var reportContainer = this.$.report_container; 256 var infobars = this.$.infobars; 257 258 function clearInfobarsOrUpdateWithErrors(mapResults) { 259 infobars.clearMessages(); 260 if (!mapResults) { 261 infobars.addMessage('Cannot map'); 262 return mapResults; 263 } 264 if (!mapResults.some(function(r) { return r.hadFailures(); })) 265 return mapResults; 266 267 infobars.addMessage( 268 'Some traces that did not process.' 269 ); 270 271 return mapResults; 272 } 273 274 var p = Promise.resolve(); 275 p = p.then(function indicateThinking() { 276 infobars.clearMessages(); 277 infobars.addMessage('... thinking...'); 278 }); 279 280 p = p.then(function doMapping() { 281 if (mappingState.currentMapTracesDriver === undefined || 282 mappingState.piReportElementName === undefined || 283 mappingState.corpusQuery === undefined) { 284 return undefined; 285 } 286 var mapFunctionName = pe.getAttribute('map-function-name'); 287 var mapFunctionHref = pe.getAttribute('map-function-href'); 288 var mapModuleToLoad = new pi.ModuleToLoad(mapFunctionHref); 289 var mapFunctionHandle = new pi.FunctionHandle( 290 [mapModuleToLoad], mapFunctionName); 291 292 var reduceFunctionName = pe.getAttribute('reduce-function-name'); 293 var reduceFunctionHref = pe.getAttribute('reduce-function-href'); 294 var reduceFunctionHandle; 295 if (reduceFunctionName && reduceFunctionHref) { 296 var reduceModuleToLoad = new pi.ModuleToLoad(reduceFunctionHref); 297 reduceFunctionHandle = new pi.FunctionHandle( 298 [reduceModuleToLoad], reduceFunctionName); 299 } 300 301 var job = new pi.mre.Job(mapFunctionHandle, reduceFunctionHandle); 302 303 if (mappingState.mapClientSide) { 304 throw new Error('Currently unsupported'); 305 } 306 307 var peCurrentDriver = document.querySelector( 308 mappingState.currentMapTracesDriver); 309 return peCurrentDriver.runMapFunction(job, mappingState.corpusQuery); 310 }); 311 p = p.then(function responseToResults(responseText) { 312 if (responseText === undefined) 313 return undefined; 314 315 var data = JSON.parse(responseText); 316 if (!data) { 317 return []; 318 } else { 319 return data.map(pi.mre.MreResult.fromDict); 320 } 321 }); 322 323 p = p.then(clearInfobarsOrUpdateWithErrors); 324 325 p = p.then(function showMappingResults(mapResults) { 326 reportContainer.textContent = ''; 327 if (mapResults === undefined) 328 return; 329 330 var reportEl; 331 if (mappingState.showRawResults) { 332 reportEl = document.createElement('pi-ui-generic-results-view'); 333 } else { 334 reportEl = document.createElement(mappingState.piReportElementName); 335 } 336 337 reportEl.mapResults = mapResults; 338 reportContainer.appendChild(reportEl); 339 }); 340 341 return p; 342 } 343 }); 344 345 return { 346 }; 347}); 348</script> 349 350