| OLD | NEW |
| (Empty) |
| 1 <!DOCTYPE HTML> | |
| 2 <html> | |
| 3 <!-- | |
| 4 Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 5 Use of this source code is governed by a BSD-style license that can be | |
| 6 found in the LICENSE file. | |
| 7 --> | |
| 8 <head> | |
| 9 <title>CQ Timeline View for issue %(issue)s patch %(patchset)s</title> | |
| 10 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | |
| 11 | |
| 12 <script src="/components/webcomponentsjs/webcomponents.js"></script> | |
| 13 <link rel="import" href="/components/polymer/polymer.html"> | |
| 14 | |
| 15 <link rel="import" href="/tracing/base/xhr.html"> | |
| 16 <link rel="import" href="/tracing/extras/full_config.html"> | |
| 17 <link rel="import" href="/tracing/importer/import.html"> | |
| 18 <link rel="import" href="/tracing/ui/timeline_view.html"> | |
| 19 | |
| 20 <style> | |
| 21 html, | |
| 22 body { | |
| 23 height: 100%%; | |
| 24 } | |
| 25 | |
| 26 body { | |
| 27 -webkit-flex-direction: column; | |
| 28 flex-direction: column; | |
| 29 display: -webkit-flex; | |
| 30 display: flex; | |
| 31 margin: 0; | |
| 32 padding: 0; | |
| 33 } | |
| 34 | |
| 35 body > tr-ui-timeline-view { | |
| 36 -webkit-flex: 1 1 auto; | |
| 37 flex: 1 1 auto; | |
| 38 min-height: 0; | |
| 39 } | |
| 40 | |
| 41 body > tr-ui-timeline-view:focus { | |
| 42 outline: none; | |
| 43 } | |
| 44 | |
| 45 </style> | |
| 46 </head> | |
| 47 <body> | |
| 48 <tr-ui-timeline-view> | |
| 49 <track-view-container id='track_view_container'></track-view-container> | |
| 50 </tr-ui-timeline-view> | |
| 51 | |
| 52 <script> | |
| 53 'use strict'; | |
| 54 | |
| 55 var timelineViewEl; | |
| 56 | |
| 57 function loadTraces(filenames, onTracesLoaded) { | |
| 58 var traces = []; | |
| 59 for (var i = 0; i < filenames.length; i++) { | |
| 60 traces.push(undefined); | |
| 61 } | |
| 62 var numTracesPending = filenames.length; | |
| 63 | |
| 64 filenames.forEach(function(filename, i) { | |
| 65 getAsync(filename, function(trace) { | |
| 66 traces[i] = trace; | |
| 67 numTracesPending--; | |
| 68 if (numTracesPending == 0) | |
| 69 onTracesLoaded(filenames, traces); | |
| 70 }); | |
| 71 }); | |
| 72 } | |
| 73 | |
| 74 function getAsync(url, cb) { | |
| 75 tr.b.getAsync(url).then(cb); | |
| 76 } | |
| 77 | |
| 78 function createViewFromTraces(filenames, traces) { | |
| 79 var m = new tr.Model(); | |
| 80 var shiftWorld = true; | |
| 81 if (timelineViewEl.model) { | |
| 82 m = timelineViewEl.model; | |
| 83 shiftWorld = false; | |
| 84 for (var traceIndex = 0; traceIndex < traces.length; traceIndex++) { | |
| 85 var eventList = JSON.parse(traces[traceIndex]); | |
| 86 for (var eventIndex = 0; eventIndex < eventList.length; eventIndex++) { | |
| 87 var traceEvent = eventList[eventIndex]; | |
| 88 traceEvent.ts = m.convertTimestampToModelTime('traceEventClock', | |
| 89 0) * 1000 + traceEvent.t
s; | |
| 90 } | |
| 91 traces[traceIndex] = JSON.stringify(eventList); | |
| 92 }; | |
| 93 } | |
| 94 var options = new tr.importer.ImportOptions(); | |
| 95 options.shiftWorldToZero = shiftWorld; | |
| 96 var i = new tr.importer.Import(m, options); | |
| 97 var p = i.importTracesWithProgressDialog(traces); | |
| 98 p.then( | |
| 99 function() { | |
| 100 timelineViewEl.model = m; | |
| 101 timelineViewEl.updateDocumentFavicon(); | |
| 102 timelineViewEl.globalMode = true; | |
| 103 timelineViewEl.viewTitle = ''; | |
| 104 | |
| 105 if (timelineViewEl.trackView) { | |
| 106 var trackView = timelineViewEl.trackView; | |
| 107 // Set the clicker to be in select mode. | |
| 108 trackView.mouseModeSelector_.mode = | |
| 109 tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION; | |
| 110 | |
| 111 // Remove the default double-click functionality. | |
| 112 trackView.removeEventListener('dblclick', trackView.onDblClick_); | |
| 113 // Define replacement double-click method. | |
| 114 var loadBuildbotData = function(e) { | |
| 115 if (this.mouseModeSelector_.mode !== | |
| 116 tr.ui.b.MOUSE_SELECTOR_MODE.SELECTION) | |
| 117 return; | |
| 118 | |
| 119 var controller = this.brushingStateController_; | |
| 120 | |
| 121 // Fetch slices currently being selected. | |
| 122 var curSelection = controller.selection; | |
| 123 if (!curSelection.length || !curSelection[0].title) | |
| 124 return; | |
| 125 | |
| 126 var build = curSelection[0]; | |
| 127 if (build.args && build.args.build_url) { | |
| 128 if (build.expanded) { | |
| 129 // TODO: Currently deleting the expanded slices isn't possible | |
| 130 } else { | |
| 131 // TODO: Fix private variable invocation, currently necessary | |
| 132 // to allow a second import. | |
| 133 timelineViewEl.model.importOptions_ = undefined; | |
| 134 var requestUrl = '/builder-timeline-data/' + build.category + | |
| 135 '/' + build.title + '/'; | |
| 136 var buildUrl = build.args.build_url; | |
| 137 var buildId = buildUrl.substr(buildUrl.indexOf('/builds/') + | |
| 138 '/builds/'.length, buildUrl.length); | |
| 139 var parentProcess = build.parentContainer.parent.pid; | |
| 140 requestUrl += buildId + '/' + parentProcess; | |
| 141 | |
| 142 loadTraces([requestUrl], createViewFromTraces); | |
| 143 build.expanded = true; | |
| 144 } | |
| 145 } else { | |
| 146 // Perform the previous default action otherwise. | |
| 147 var selection = new tr.model.EventSet(); | |
| 148 var filter = new tr.c.ExactTitleFilter(curSelection[0].title); | |
| 149 this.modelTrack_.addAllEventsMatchingFilterToSelection(filter, | |
| 150 selection); | |
| 151 | |
| 152 controller.changeSelectionFromTimeline(selection); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 var onDblClick = loadBuildbotData.bind(trackView); | |
| 157 trackView.addEventListener('dblclick', onDblClick); | |
| 158 } | |
| 159 }, | |
| 160 function(err) { | |
| 161 var overlay = new tr.ui.b.Overlay(); | |
| 162 overlay.textContent = tr.b.normalizeException(err).message; | |
| 163 overlay.title = 'Import error'; | |
| 164 overlay.visible = true; | |
| 165 } | |
| 166 ); | |
| 167 } | |
| 168 | |
| 169 function cleanFilename(file) { | |
| 170 var m = /\/test_data\/(.+)/.exec(file); | |
| 171 var rest = m[1]; | |
| 172 | |
| 173 function upcase(letter) { | |
| 174 return ' ' + letter.toUpperCase(); | |
| 175 } | |
| 176 | |
| 177 return rest.replace(/_/g, ' ') | |
| 178 .replace(/\.[^\.]*$/, '') | |
| 179 .replace(/ ([a-z])/g, upcase) | |
| 180 .replace(/^[a-z]/, upcase); | |
| 181 } | |
| 182 | |
| 183 function onLoad() { | |
| 184 timelineViewEl = document.querySelector('tr-ui-timeline-view'); | |
| 185 timelineViewEl.globalMode = true; | |
| 186 | |
| 187 var instrEl = document.createElement('div'); | |
| 188 instrEl.innerHTML = 'Double click on a builder to display build data.' + | |
| 189 'Build data may take up to 30 minutes to be available.'; | |
| 190 timelineViewEl.leftControls.appendChild(instrEl); | |
| 191 | |
| 192 var file = "/patch-timeline-data/%(issue)s/%(patchset)s"; | |
| 193 var filenames = [file]; | |
| 194 loadTraces(filenames, createViewFromTraces); | |
| 195 } | |
| 196 window.addEventListener('load', onLoad); | |
| 197 | |
| 198 // Add Google Analytics integration | |
| 199 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
| 200 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o
), | |
| 201 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a
,m) | |
| 202 })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | |
| 203 | |
| 204 ga('create', 'UA-55762617-13', {'siteSpeedSampleRate': 100}); | |
| 205 ga('send', 'pageview'); | |
| 206 </script> | |
| 207 </body> | |
| 208 </html> | |
| OLD | NEW |