Changeset 205578 in webkit
- Timestamp:
- Sep 7, 2016 6:04:48 PM (8 years ago)
- Location:
- trunk/Source/WebInspectorUI
- Files:
-
- 2 added
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebInspectorUI/ChangeLog
r205555 r205578 1 2016-09-07 Johan K. Jensen <johan_jensen@apple.com> 2 3 Web Inspector: Show resource timing details in Network waterfall 4 https://bugs.webkit.org/show_bug.cgi?id=160062 5 6 This patch adds a popover to network tab's and timeline tab's resource rows, when hovering 7 the recordbar. It shows times for the various parts of the resource load if they are available, 8 or it shows the reason for why not as text (e.g. cached resource, data URI). 9 10 Reviewed by Matt Baker. 11 12 * Localizations/en.lproj/localizedStrings.js: 13 Added new strings. 14 15 * UserInterface/Main.html: 16 Include new files. 17 18 * UserInterface/Views/ComputedStyleDetailsPanel.js: 19 (WebInspector.ComputedStyleDetailsPanel.prototype.initialLayout): 20 Updated to use DataGrid property to hide header. 21 22 * UserInterface/Views/DataGrid.css: 23 (.data-grid.no-header > .header-wrapper): 24 (.data-grid.no-header > .header-wrapper > table.header): Deleted. 25 Hide the whole DataGrid header. 26 27 * UserInterface/Views/DataGrid.js: 28 (WebInspector.DataGrid): 29 (WebInspector.DataGrid.prototype.get headerVisible): 30 (WebInspector.DataGrid.prototype.set headerVisible): 31 Add property to control header visibility, so grid clients aren't forced to manipulate internal grid styles. 32 33 * UserInterface/Views/NetworkGridContentView.js: 34 (WebInspector.NetworkGridContentView.prototype._processPendingRecords): 35 * UserInterface/Views/NetworkTimelineView.js: 36 (WebInspector.NetworkTimelineView.prototype._processPendingRecords): 37 * UserInterface/Views/OverviewTimelineView.js: 38 (WebInspector.OverviewTimelineView.prototype._addResourceToDataGridIfNeeded): 39 Pass in new parameter for whether or not to show the popover for resources. 40 41 * UserInterface/Views/ResourceTimelineDataGridNode.css: Added. 42 (.resource-timing-popover-content .data-grid): 43 (.resource-timing-popover-content .data-grid .graph-column > .cell-content): 44 (.resource-timing-popover-content .data-grid td): 45 (.resource-timing-popover-content .data-grid td.graph-column): 46 (.resource-timing-popover-content .data-grid table.data): 47 (.resource-timing-popover-content .data-grid tr:nth-last-child(2)): 48 Add styling for the popover. 49 50 * UserInterface/Views/ResourceTimelineDataGridNode.js: 51 (WebInspector.ResourceTimelineDataGridNode): 52 (WebInspector.ResourceTimelineDataGridNode.prototype.didAddRecordBar): 53 (WebInspector.ResourceTimelineDataGridNode.prototype.didRemoveRecordBar): 54 (WebInspector.ResourceTimelineDataGridNode.prototype._mouseoverRecordBar): 55 Creates the popover for a RecordBar and shows detailed info about segments. 56 57 * UserInterface/Views/ResourceTimingPopoverDataGridNode.js: Added. 58 (WebInspector.ResourceTimingPopoverDataGridNode): 59 (WebInspector.ResourceTimingPopoverDataGridNode.prototype.get records): 60 (WebInspector.ResourceTimingPopoverDataGridNode.prototype.get data): 61 (WebInspector.ResourceTimingPopoverDataGridNode.prototype.get selectable): 62 (WebInspector.ResourceTimingPopoverDataGridNode.prototype.createCellContent): 63 DataGridNode for the popover. 64 65 * UserInterface/Views/TimelineDataGridNode.js: 66 (WebInspector.TimelineDataGridNode.prototype.refreshGraph.createBar): 67 (WebInspector.TimelineDataGridNode.prototype.refreshGraph): 68 (WebInspector.TimelineDataGridNode.prototype.didAddRecordBar): 69 (WebInspector.TimelineDataGridNode.prototype.didRemoveRecordBar): 70 (WebInspector.TimelineDataGridNode): 71 Added hooks for notifying subclasses. 72 73 * UserInterface/Views/TimelineRecordBar.js: 74 (WebInspector.TimelineRecordBar): 75 (WebInspector.TimelineRecordBar.fromElement): 76 Added symbol and static method to get the recordBar from a given element. 77 1 78 2016-09-07 Devin Rousso <dcrousso+webkit@gmail.com> 2 79 -
trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
r205518 r205578 185 185 localizedStrings["Condition"] = "Condition"; 186 186 localizedStrings["Conditional expression"] = "Conditional expression"; 187 localizedStrings["Connection"] = "Connection"; 187 188 localizedStrings["Console"] = "Console"; 188 189 localizedStrings["Console Evaluation"] = "Console Evaluation"; … … 214 215 localizedStrings["Current"] = "Current"; 215 216 localizedStrings["Cursor"] = "Cursor"; 217 localizedStrings["DNS"] = "DNS"; 216 218 localizedStrings["DOM Content Loaded \u2014 %s"] = "DOM Content Loaded \u2014 %s"; 217 219 localizedStrings["Damping"] = "Damping"; … … 605 607 localizedStrings["Resource"] = "Resource"; 606 608 localizedStrings["Resource Type"] = "Resource Type"; 609 localizedStrings["Resource failed to load."] = "Resource failed to load."; 610 localizedStrings["Resource was loaded with the 'data' scheme."] = "Resource was loaded with the 'data' scheme."; 611 localizedStrings["Resource was served from the cache."] = "Resource was served from the cache."; 607 612 localizedStrings["Resources"] = "Resources"; 608 613 localizedStrings["Response"] = "Response"; … … 691 696 localizedStrings["Spread"] = "Spread"; 692 697 localizedStrings["Spring"] = "Spring"; 698 localizedStrings["Stalled"] = "Stalled"; 693 699 localizedStrings["Start Playback"] = "Start Playback"; 694 700 localizedStrings["Start Recording"] = "Start Recording"; … … 757 763 localizedStrings["Total number of resources, click to show the Resources tab"] = "Total number of resources, click to show the Resources tab"; 758 764 localizedStrings["Total size of all resources, click to show the Network Requests timeline"] = "Total size of all resources, click to show the Network Requests timeline"; 765 localizedStrings["Total time"] = "Total time"; 759 766 localizedStrings["Trace"] = "Trace"; 760 767 localizedStrings["Trace: %s"] = "Trace: %s"; -
trunk/Source/WebInspectorUI/UserInterface/Main.html
r205418 r205578 137 137 <link rel="stylesheet" href="Views/ResourceIcons.css"> 138 138 <link rel="stylesheet" href="Views/ResourceSidebarPanel.css"> 139 <link rel="stylesheet" href="Views/ResourceTimelineDataGridNode.css"> 139 140 <link rel="stylesheet" href="Views/ResourceTreeElement.css"> 140 141 <link rel="stylesheet" href="Views/RulesStyleDetailsPanel.css"> … … 605 606 <script src="Views/ResourceSidebarPanel.js"></script> 606 607 <script src="Views/ResourceTimelineDataGridNode.js"></script> 608 <script src="Views/ResourceTimingPopoverDataGridNode.js"></script> 607 609 <script src="Views/RulesStyleDetailsPanel.js"></script> 608 610 <script src="Views/ScopeBar.js"></script> -
trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.js
r205555 r205578 200 200 201 201 this._containerRegionsDataGrid = new WebInspector.DOMTreeDataGrid; 202 this._containerRegionsDataGrid. element.classList.add("no-header");202 this._containerRegionsDataGrid.headerVisible = false; 203 203 204 204 let containerRegionsRow = new WebInspector.DetailsSectionDataGridRow(this._containerRegionsDataGrid); -
trunk/Source/WebInspectorUI/UserInterface/Views/DataGrid.css
r204518 r205578 67 67 } 68 68 69 .data-grid.no-header > .header-wrapper > table.header{69 .data-grid.no-header > .header-wrapper { 70 70 display: none; 71 71 } -
trunk/Source/WebInspectorUI/UserInterface/Views/DataGrid.js
r205426 r205578 40 40 this._hiddenColumnSetting = null; 41 41 this._columnChooserEnabled = false; 42 this._headerVisible = true; 42 43 43 44 this._rows = []; … … 208 209 209 210 return dataGrid; 211 } 212 213 get headerVisible() { return this._headerVisible; } 214 215 set headerVisible(x) 216 { 217 if (x === this._headerVisible) 218 return; 219 220 this._headerVisible = x; 221 this.element.classList.toggle("no-header", !this._headerVisible); 210 222 } 211 223 -
trunk/Source/WebInspectorUI/UserInterface/Views/NetworkGridContentView.js
r204579 r205578 210 210 211 211 treeElement = new WebInspector.ResourceTreeElement(resourceTimelineRecord.resource); 212 var dataGridNode = new WebInspector.ResourceTimelineDataGridNode(resourceTimelineRecord, false, this); 212 213 const includesGraph = false; 214 const shouldShowPopover = true; 215 let dataGridNode = new WebInspector.ResourceTimelineDataGridNode(resourceTimelineRecord, includesGraph, this, shouldShowPopover); 213 216 214 217 this._dataGrid.addRowInSortOrder(treeElement, dataGridNode); -
trunk/Source/WebInspectorUI/UserInterface/Views/NetworkTimelineView.js
r205320 r205578 242 242 continue; 243 243 244 dataGridNode = new WebInspector.ResourceTimelineDataGridNode(resourceTimelineRecord, false, this); 244 const includesGraph = false; 245 const shouldShowPopover = true; 246 dataGridNode = new WebInspector.ResourceTimelineDataGridNode(resourceTimelineRecord, includesGraph, this, shouldShowPopover); 245 247 this._resourceDataGridNodeMap.set(resourceTimelineRecord.resource, dataGridNode); 246 248 -
trunk/Source/WebInspectorUI/UserInterface/Views/OverviewTimelineView.js
r205425 r205578 212 212 resourceTimelineRecord = new WebInspector.ResourceTimelineRecord(resource); 213 213 214 let resourceDataGridNode = new WebInspector.ResourceTimelineDataGridNode(resourceTimelineRecord, true, this); 214 const includesGraph = true; 215 const shouldShowPopover = false; 216 let resourceDataGridNode = new WebInspector.ResourceTimelineDataGridNode(resourceTimelineRecord, includesGraph, this, shouldShowPopover); 215 217 this._resourceDataGridNodeMap.set(resource, resourceDataGridNode); 216 218 -
trunk/Source/WebInspectorUI/UserInterface/Views/ResourceTimelineDataGridNode.js
r205151 r205578 26 26 WebInspector.ResourceTimelineDataGridNode = class ResourceTimelineDataGridNode extends WebInspector.TimelineDataGridNode 27 27 { 28 constructor(resourceTimelineRecord, includesGraph, graphDataSource )28 constructor(resourceTimelineRecord, includesGraph, graphDataSource, shouldShowPopover) 29 29 { 30 30 super(includesGraph, graphDataSource); … … 32 32 this._resource = resourceTimelineRecord.resource; 33 33 this._record = resourceTimelineRecord; 34 this._shouldShowPopover = shouldShowPopover; 34 35 35 36 this._resource.addEventListener(WebInspector.Resource.Event.LoadingDidFinish, this._needsRefresh, this); … … 154 155 // Protected 155 156 157 didAddRecordBar(recordBar) 158 { 159 if (!this._shouldShowPopover) 160 return; 161 162 if (!recordBar.records.length || recordBar.records[0].type !== WebInspector.TimelineRecord.Type.Network) 163 return; 164 165 console.assert(!this._mouseEnterRecordBarListener); 166 this._mouseEnterRecordBarListener = this._mouseoverRecordBar.bind(this); 167 recordBar.element.addEventListener("mouseenter", this._mouseEnterRecordBarListener); 168 } 169 170 didRemoveRecordBar(recordBar) 171 { 172 if (!this._shouldShowPopover) 173 return; 174 175 if (!recordBar.records.length || recordBar.records[0].type !== WebInspector.TimelineRecord.Type.Network) 176 return; 177 178 recordBar.element.removeEventListener("mouseenter", this._mouseEnterRecordBarListener); 179 this._mouseEnterRecordBarListener = null; 180 } 181 156 182 filterableDataForColumn(columnIdentifier) 157 183 { … … 241 267 contentElement.appendChild(this._spinner.element); 242 268 } 269 270 _mouseoverRecordBar(event) 271 { 272 let recordBar = WebInspector.TimelineRecordBar.fromElement(event.target); 273 console.assert(recordBar); 274 if (!recordBar) 275 return; 276 277 let calculateTargetFrame = () => { 278 let columnRect = WebInspector.Rect.rectFromClientRect(this.elementWithColumnIdentifier("graph").getBoundingClientRect()); 279 let barRect = WebInspector.Rect.rectFromClientRect(event.target.getBoundingClientRect()); 280 return columnRect.intersectionWithRect(barRect); 281 }; 282 283 let targetFrame = calculateTargetFrame(); 284 if (!targetFrame.size.width && !targetFrame.size.height) 285 return; 286 287 console.assert(recordBar.records.length); 288 let resource = recordBar.records[0].resource; 289 if (!resource.timingData) 290 return; 291 292 if (!resource.timingData.responseEnd) 293 return; 294 295 if (this.dataGrid._dismissPopoverTimeout) { 296 clearTimeout(this.dataGrid._dismissPopoverTimeout); 297 this.dataGrid._dismissPopoverTimeout = undefined; 298 } 299 300 let popoverContentElement = document.createElement("div"); 301 popoverContentElement.classList.add("resource-timing-popover-content"); 302 303 if (resource.failed || resource.urlComponents.scheme === "data" || (resource.cached && resource.statusCode !== 304)) { 304 let descriptionElement = document.createElement("span"); 305 descriptionElement.classList.add("description"); 306 if (resource.failed) 307 descriptionElement.textContent = WebInspector.UIString("Resource failed to load."); 308 else if (resource.urlComponents.scheme === "data") 309 descriptionElement.textContent = WebInspector.UIString("Resource was loaded with the 'data' scheme."); 310 else 311 descriptionElement.textContent = WebInspector.UIString("Resource was served from the cache."); 312 popoverContentElement.appendChild(descriptionElement); 313 } else { 314 let columns = { 315 description: { 316 width: "80px" 317 }, 318 graph: { 319 width: `${WebInspector.ResourceTimelineDataGridNode.PopoverGraphColumnWidthPixels}px` 320 }, 321 duration: { 322 width: "70px", 323 aligned: "right" 324 } 325 }; 326 327 let popoverDataGrid = new WebInspector.DataGrid(columns); 328 popoverDataGrid.inline = true; 329 popoverDataGrid.headerVisible = false; 330 popoverContentElement.appendChild(popoverDataGrid.element); 331 332 let graphDataSource = { 333 get secondsPerPixel() { return resource.duration / WebInspector.ResourceTimelineDataGridNode.PopoverGraphColumnWidthPixels; }, 334 get zeroTime() { return resource.firstTimestamp; }, 335 get startTime() { return resource.firstTimestamp; }, 336 get currentTime() { return this.endTime; }, 337 338 get endTime() 339 { 340 let endTimePadding = this.secondsPerPixel * WebInspector.TimelineRecordBar.MinimumWidthPixels; 341 return resource.lastTimestamp + endTimePadding; 342 } 343 }; 344 345 let secondTimestamp = resource.timingData.domainLookupStart || resource.timingData.connectStart || resource.timingData.requestStart; 346 if (secondTimestamp - resource.timingData.startTime) 347 popoverDataGrid.appendChild(new WebInspector.ResourceTimingPopoverDataGridNode(WebInspector.UIString("Stalled"), resource.timingData.startTime, secondTimestamp, graphDataSource)); 348 if (resource.timingData.domainLookupStart) 349 popoverDataGrid.appendChild(new WebInspector.ResourceTimingPopoverDataGridNode(WebInspector.UIString("DNS"), resource.timingData.domainLookupStart, resource.timingData.domainLookupEnd, graphDataSource)); 350 if (resource.timingData.connectStart) 351 popoverDataGrid.appendChild(new WebInspector.ResourceTimingPopoverDataGridNode(WebInspector.UIString("Connection"), resource.timingData.connectStart, resource.timingData.connectEnd, graphDataSource)); 352 if (resource.timingData.secureConnectionStart) 353 popoverDataGrid.appendChild(new WebInspector.ResourceTimingPopoverDataGridNode(WebInspector.UIString("Secure"), resource.timingData.secureConnectionStart, resource.timingData.connectEnd, graphDataSource)); 354 popoverDataGrid.appendChild(new WebInspector.ResourceTimingPopoverDataGridNode(WebInspector.UIString("Request"), resource.timingData.requestStart, resource.timingData.responseStart, graphDataSource)); 355 popoverDataGrid.appendChild(new WebInspector.ResourceTimingPopoverDataGridNode(WebInspector.UIString("Response"), resource.timingData.responseStart, resource.timingData.responseEnd, graphDataSource)); 356 357 const higherResolution = true; 358 let totalData = { 359 description: WebInspector.UIString("Total time"), 360 duration: Number.secondsToMillisecondsString(resource.timingData.responseEnd - resource.timingData.startTime, higherResolution) 361 }; 362 popoverDataGrid.appendChild(new WebInspector.DataGridNode(totalData)); 363 364 popoverDataGrid.updateLayout(); 365 } 366 367 if (!this.dataGrid._popover) 368 this.dataGrid._popover = new WebInspector.Popover; 369 370 let preferredEdges = [WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MIN_X]; 371 this.dataGrid._popover.windowResizeHandler = () => { 372 let bounds = calculateTargetFrame(); 373 this.dataGrid._popover.present(bounds.pad(2), preferredEdges); 374 }; 375 376 recordBar.element.addEventListener("mouseleave", () => { 377 this.dataGrid._dismissPopoverTimeout = setTimeout(() => this.dataGrid._popover.dismiss(), WebInspector.ResourceTimelineDataGridNode.DelayedPopoverDismissalTimeout); 378 }, {once: true}); 379 380 this.dataGrid._popover.presentNewContentWithFrame(popoverContentElement, targetFrame.pad(2), preferredEdges); 381 } 243 382 }; 383 384 WebInspector.ResourceTimelineDataGridNode.PopoverGraphColumnWidthPixels = 110; 385 WebInspector.ResourceTimelineDataGridNode.DelayedPopoverDismissalTimeout = 500; -
trunk/Source/WebInspectorUI/UserInterface/Views/TimelineDataGridNode.js
r205425 r205578 242 242 } 243 243 timelineRecordBar.refresh(this._graphDataSource); 244 if (!timelineRecordBar.element.parentNode) 244 if (!timelineRecordBar.element.parentNode) { 245 245 this._graphContainerElement.appendChild(timelineRecordBar.element); 246 this.didAddRecordBar(timelineRecordBar); 247 } 246 248 ++recordBarIndex; 247 249 } … … 285 287 // Remove the remaining unused TimelineRecordBars. 286 288 for (; recordBarIndex < this._timelineRecordBars.length; ++recordBarIndex) { 289 this._timelineRecordBars[recordBarIndex].element.remove(); 290 this.didRemoveRecordBar(this._timelineRecordBars[recordBarIndex]); 287 291 this._timelineRecordBars[recordBarIndex].records = null; 288 this._timelineRecordBars[recordBarIndex].element.remove();289 292 } 290 293 } … … 377 380 return super.filterableDataForColumn(columnIdentifier); 378 381 } 382 383 didAddRecordBar(recordBar) 384 { 385 // Implemented by subclasses. 386 } 387 388 didRemoveRecordBar(recordBar) 389 { 390 // Implemented by subclasses. 391 } 379 392 }; -
trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordBar.js
r203920 r205578 32 32 this._element = document.createElement("div"); 33 33 this._element.classList.add("timeline-record-bar"); 34 this._element[WebInspector.TimelineRecordBar.ElementReferenceSymbol] = this; 34 35 35 36 this.renderMode = renderMode; … … 165 166 if (!isNaN(activeStartTime)) 166 167 createBarCallback(activeRecords, WebInspector.TimelineRecordBar.RenderMode.ActiveOnly); 168 } 169 170 static fromElement(element) 171 { 172 return element[WebInspector.TimelineRecordBar.ElementReferenceSymbol] || null; 167 173 } 168 174 … … 363 369 }; 364 370 371 WebInspector.TimelineRecordBar.ElementReferenceSymbol = Symbol("timeline-record-bar"); 372 365 373 WebInspector.TimelineRecordBar.MinimumWidthPixels = 4; 366 374 WebInspector.TimelineRecordBar.MinimumMarginPixels = 1;
Note: See TracChangeset
for help on using the changeset viewer.