Changeset 242562 in webkit
- Timestamp:
- Mar 6, 2019, 12:38:43 PM (6 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r242551 r242562 1 2019-03-06 Joseph Pecoraro <pecoraro@apple.com> 2 3 Web Inspector: CPU Usage Timeline - Statistics and Sources sections 4 https://bugs.webkit.org/show_bug.cgi?id=195202 5 6 Reviewed by Devin Rousso. 7 8 * inspector/unit-tests/map-utilities-expected.txt: Added. 9 * inspector/unit-tests/map-utilities.html: Added. 10 * inspector/unit-tests/set-utilities-expected.txt: 11 * inspector/unit-tests/set-utilities.html: 12 1 13 2019-03-06 Wenson Hsieh <wenson_hsieh@apple.com> 2 14 -
trunk/LayoutTests/inspector/unit-tests/set-utilities-expected.txt
r241652 r242562 1 1 2 == Running test suite: Set Utilities2 == Running test suite: Set 3 3 -- Running test case: Set.prototype.intersects 4 4 PASS: an empty set should not intersect another empty set. -
trunk/LayoutTests/inspector/unit-tests/set-utilities.html
r241652 r242562 6 6 function test() 7 7 { 8 let suite = InspectorTest.createSyncSuite("Set Utilities");8 let suite = InspectorTest.createSyncSuite("Set"); 9 9 10 10 suite.addTestCase({ … … 29 29 testTrue([1, "a", object1], [1, 3, "a", "c", object1, object3], "a set should intersect another set with same and additional values."); 30 30 testTrue([1, 2, "a", "b", object1, object2], [1, 3, "a", "c", object1, object3], "a set should intersect another set with same and different values."); 31 32 return true;33 31 } 34 32 }); … … 55 53 testTrue([1, "a", object1], [1, 3, "a", "c", object1, object3], "a set should be a subset of another set with same and additional values."); 56 54 testFalse([1, 2, "a", "b", object1, object2], [1, 3, "a", "c", object1, object3], "a set should not be a subset of another set with same and different values."); 57 58 return true;59 55 } 60 56 }); … … 80 76 testFalse([1, "a", object1], [2, "b", object2], "a set should not be a equal to another set with different values."); 81 77 testFalse([1, 2, "a", "b", object1, object2], [1, 3, "a", "c", object1, object3], "a set should not be equal to another set with same and different values."); 82 83 return true;84 78 } 85 79 }); … … 122 116 expectedDifference: [1], 123 117 }); 124 125 return true;126 118 } 127 119 }); -
trunk/Source/WebInspectorUI/ChangeLog
r242560 r242562 1 2019-03-06 Joseph Pecoraro <pecoraro@apple.com> 2 3 Web Inspector: CPU Usage Timeline - Statistics and Sources sections 4 https://bugs.webkit.org/show_bug.cgi?id=195202 5 6 Reviewed by Devin Rousso. 7 8 * Localizations/en.lproj/localizedStrings.js: 9 New strings. 10 11 * UserInterface/Base/Utilities.js: 12 (Map.prototype.getOrInitialize): 13 Helper to get and if not found initialize with a value. 14 15 * UserInterface/Views/CPUTimelineView.css: 16 (.timeline-view.cpu > .content > .overview > .chart > .container.stats): 17 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table): 18 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table > tr > th): 19 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table > tr > td.number): 20 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table > tr > td.label): 21 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table .show-more): 22 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table .filter): 23 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table .filter:hover): 24 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table .active): 25 (.timeline-view.cpu > .content > .overview > .chart > .container.stats > table .active + .active): 26 (@media (prefers-color-scheme: dark)): 27 Colors for the statistics sections. 28 29 * UserInterface/Views/CPUTimelineView.js: 30 (WI.CPUTimelineView): 31 (WI.CPUTimelineView.prototype.reset): 32 (WI.CPUTimelineView.prototype.clear): 33 (WI.CPUTimelineView.prototype._clearStatistics): 34 (WI.CPUTimelineView.prototype._clearSources): 35 Updates for additional sections. 36 Include a cache of the statisiticsData so we can relayout parts of the UI and 37 avoid an entire UI update. 38 39 (WI.CPUTimelineView.prototype.initialLayout): 40 (WI.CPUTimelineView.prototype._layoutBreakdownChart): 41 (WI.CPUTimelineView.prototype._layoutStatisticsAndSources): 42 (WI.CPUTimelineView.prototype._layoutStatisticsSection.createEllipsisElement): 43 (WI.CPUTimelineView.prototype._layoutStatisticsSection): 44 (WI.CPUTimelineView.prototype._layoutSourcesSection.firstNonNativeCallFrame): 45 (WI.CPUTimelineView.prototype._layoutSourcesSection.keyForSourceCodeLocation): 46 (WI.CPUTimelineView.prototype._layoutSourcesSection.labelForLocation): 47 (WI.CPUTimelineView.prototype._layoutSourcesSection.createEllipsisElement): 48 (WI.CPUTimelineView.prototype._layoutSourcesSection): 49 Extract layouts into helper methods to avoid an enormous layout method. 50 51 (WI.CPUTimelineView.prototype._computeSamplingData.incrementTypeCount): 52 (WI.CPUTimelineView.prototype._computeSamplingData): 53 Compute additional data when going through script events. 54 55 (WI.CPUTimelineView.prototype._resetSourcesFilters): 56 (WI.CPUTimelineView.prototype._addSourcesFilter): 57 (WI.CPUTimelineView.prototype._removeSourcesFilter): 58 (WI.CPUTimelineView.prototype._updateSourcesFilters): 59 Helpers for updating the source filters. 60 61 (WI.CPUTimelineView.prototype._createTableRow): 62 (WI.CPUTimelineView.prototype._insertTableRow): 63 Helpers for creating rows in the statistics / sources tables. 64 1 65 2019-03-06 Joseph Pecoraro <pecoraro@apple.com> 2 66 -
trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
r242556 r242562 123 123 localizedStrings["Anonymous StyleSheet %d"] = "Anonymous StyleSheet %d"; 124 124 localizedStrings["Application Cache"] = "Application Cache"; 125 /* Approximate count of events */ 126 localizedStrings["Approximate Number"] = "~%s"; 125 127 localizedStrings["Area"] = "Area"; 126 128 localizedStrings["Assertion"] = "Assertion"; … … 428 430 localizedStrings["Event Breakpoint\u2026"] = "Event Breakpoint\u2026"; 429 431 localizedStrings["Event Dispatched"] = "Event Dispatched"; 432 localizedStrings["Event Handlers:"] = "Event Handlers:"; 430 433 localizedStrings["Event Listeners"] = "Event Listeners"; 431 434 localizedStrings["Events"] = "Events"; 435 localizedStrings["Events:"] = "Events:"; 432 436 localizedStrings["Example: \u201C%s\u201D"] = "Example: \u201C%s\u201D"; 433 437 localizedStrings["Exception with thrown value: %s"] = "Exception with thrown value: %s"; … … 458 462 localizedStrings["Filter"] = "Filter"; 459 463 localizedStrings["Filter Full URL"] = "Filter Full URL"; 464 localizedStrings["Filter:"] = "Filter:"; 460 465 localizedStrings["Find Next (%s)"] = "Find Next (%s)"; 461 466 localizedStrings["Find Previous (%s)"] = "Find Previous (%s)"; … … 687 692 localizedStrings["Object Store"] = "Object Store"; 688 693 localizedStrings["Observer Callback"] = "Observer Callback"; 694 localizedStrings["Observer Handlers:"] = "Observer Handlers:"; 695 localizedStrings["Observers:"] = "Observers:"; 689 696 localizedStrings["Off"] = "Off"; 690 697 localizedStrings["Once"] = "Once"; … … 854 861 localizedStrings["Script"] = "Script"; 855 862 localizedStrings["Script Element %d"] = "Script Element %d"; 863 localizedStrings["Script Entries:"] = "Script Entries:"; 856 864 localizedStrings["Script Evaluated"] = "Script Evaluated"; 857 865 localizedStrings["Scripts"] = "Scripts"; … … 957 965 localizedStrings["Start to Finish"] = "Start to Finish"; 958 966 localizedStrings["State"] = "State"; 967 localizedStrings["Statistics"] = "Statistics"; 959 968 localizedStrings["Status"] = "Status"; 960 969 localizedStrings["Step"] = "Step"; … … 1034 1043 localizedStrings["Timer Installed"] = "Timer Installed"; 1035 1044 localizedStrings["Timer Removed"] = "Timer Removed"; 1045 localizedStrings["Timers:"] = "Timers:"; 1036 1046 localizedStrings["Timestamp \u2014 %s"] = "Timestamp \u2014 %s"; 1037 1047 localizedStrings["Timing"] = "Timing"; … … 1064 1074 localizedStrings["Undefined custom element"] = "Undefined custom element"; 1065 1075 localizedStrings["Unique"] = "Unique"; 1076 localizedStrings["Unknown Location"] = "Unknown Location"; 1066 1077 localizedStrings["Unknown error"] = "Unknown error"; 1067 1078 localizedStrings["Unknown node"] = "Unknown node"; -
trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js
r242174 r242562 113 113 value(key) 114 114 { 115 vardeletedValue = this.get(key);115 let deletedValue = this.get(key); 116 116 this.delete(key); 117 117 return deletedValue; 118 } 119 }); 120 121 Object.defineProperty(Map.prototype, "getOrInitialize", 122 { 123 value(key, initialValue) 124 { 125 console.assert(initialValue !== undefined, "getOrInitialize should not be used with undefined."); 126 127 let value = this.get(key); 128 if (value) 129 return value; 130 131 this.set(key, initialValue); 132 return initialValue; 118 133 } 119 134 }); -
trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js
r240457 r242562 109 109 110 110 // Return the first non-native code call frame as the initiator. 111 for (var i = 0; i < this._callFrames.length; ++i) { 112 if (this._callFrames[i].nativeCode) 113 continue; 114 return this._callFrames[i]; 111 for (let frame of this._callFrames) { 112 if (!frame.nativeCode) 113 return frame; 115 114 } 116 115 -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css
r242300 r242562 46 46 height: 15px; 47 47 -webkit-margin-start: 7px; 48 color: white;49 48 font-size: 12px; 50 background-color: darkgray; 49 color: var(--gray-foreground-color); 50 background-color: var(--gray-background-color); 51 51 border-radius: 50%; 52 52 } … … 319 319 } 320 320 321 .timeline-view.cpu > .content > .overview > .chart > .container.stats { 322 padding: 0 5px; 323 white-space: nowrap; 324 -webkit-user-select: text; 325 } 326 327 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table { 328 overflow: hidden; 329 } 330 331 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table > tr > th { 332 text-align: end; 333 } 334 335 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table > tr > td.number { 336 min-width: 25px; 337 padding: 0px 2px; 338 text-align: end; 339 } 340 341 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table > tr > td.label { 342 text-align: start; 343 } 344 345 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table .show-more { 346 cursor: pointer; 347 } 348 349 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table .unknown { 350 color: var(--link-text-color); 351 } 352 353 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table .filter-clear { 354 display: inline-block; 355 width: 13px; 356 height: 13px; 357 font-size: 12px; 358 color: var(--gray-foreground-color); 359 background-color: var(--gray-background-color); 360 border-radius: 50%; 361 line-height: 12px; 362 text-align: center; 363 cursor: pointer; 364 } 365 366 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table .filter { 367 padding: 0 6px 1px; 368 font-size: 10px; 369 background-color: hsl(0, 0%, 85%); 370 border: 1px solid transparent; 371 border-radius: 3px; 372 cursor: pointer; 373 } 374 375 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table :matches(.filter, .filter-clear):hover { 376 opacity: 0.7; 377 } 378 379 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table .filter.active { 380 color: var(--selected-foreground-color); 381 background-color: var(--selected-background-color); 382 } 383 384 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table .filter.active + .filter.active { 385 -webkit-margin-start: 3px; 386 } 387 321 388 @media (prefers-color-scheme: dark) { 322 .timeline-view.cpu > .content .subtitle > .info {323 background-color: gray;324 }325 326 389 .timeline-view.cpu .gauge-chart:not(.empty) > svg > polygon.needle { 327 390 fill: hsla(0, 0%, var(--foreground-lightness), 0.85); 328 391 stroke: hsla(0, 0%, var(--foreground-lightness), 0.85); 329 392 } 330 } 393 394 .timeline-view.cpu > .content > .overview > .chart > .container.stats > table .filter { 395 background-color: hsl(0, 0%, 33%); 396 } 397 } -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js
r242300 r242562 36 36 this.element.classList.add("cpu"); 37 37 38 this._statisticsData = null; 39 this._sectionLimit = CPUTimelineView.defaultSectionLimit; 40 38 41 timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._cpuTimelineRecordAdded, this); 39 42 } … … 64 67 static get highEnergyThreshold() { return 150; } 65 68 69 static get defaultSectionLimit() { return 5; } 70 66 71 // Public 67 72 … … 83 88 { 84 89 super.reset(); 90 91 this._resetSourcesFilters(); 85 92 86 93 this.clear(); … … 99 106 this._energyChart.needsLayout(); 100 107 this._clearEnergyImpactText(); 108 109 this._clearStatistics(); 110 this._clearSources(); 101 111 102 112 function clearUsageView(view) { … … 116 126 117 127 this._mainThreadWorkIndicatorView.clear(); 128 129 this._statisticsData = null; 130 this._sectionLimit = CPUTimelineView.defaultSectionLimit; 118 131 } 119 132 … … 146 159 chartSubtitleElement.classList.add("subtitle"); 147 160 chartSubtitleElement.textContent = subtitle; 148 chartSubtitleElement.title = tooltip; 161 if (tooltip) 162 chartSubtitleElement.title = tooltip; 149 163 150 164 let chartFlexContainerElement = chartElement.appendChild(document.createElement("div")); … … 190 204 dividerElement.classList.add("divider"); 191 205 192 let energyTooltip = WI.UIString("Estimated energy impact.") 193 let energyContainerElement = createChartContainer(overviewElement, WI.UIString("Energy Impact"), energyTooltip); 206 let energyContainerElement = createChartContainer(overviewElement, WI.UIString("Energy Impact"), WI.UIString("Estimated energy impact.")); 194 207 energyContainerElement.classList.add("energy"); 195 208 … … 289 302 this._threadsDetailsElement.addEventListener("toggle", (event) => { 290 303 WI.settings.cpuTimelineThreadDetailsExpanded.value = this._threadsDetailsElement.open; 291 this.updateLayout(); 304 if (this._threadsDetailsElement.open) 305 this.updateLayout(WI.CPUTimelineView.LayoutReason.Internal); 292 306 }); 293 307 … … 313 327 this._workerViews = []; 314 328 329 this._sourcesFilter = { 330 timer: new Set, 331 event: new Set, 332 observer: new Set, 333 }; 334 335 let bottomOverviewElement = contentElement.appendChild(document.createElement("div")); 336 bottomOverviewElement.classList.add("overview"); 337 338 let statisticsContainerElement = createChartContainer(bottomOverviewElement, WI.UIString("Statistics")); 339 statisticsContainerElement.classList.add("stats"); 340 341 this._statisticsTable = statisticsContainerElement.appendChild(document.createElement("table")); 342 this._statisticsRows = []; 343 344 { 345 let {headerCell, numberCell} = this._createTableRow(this._statisticsTable); 346 headerCell.textContent = WI.UIString("Script Entries:"); 347 this._scriptEntriesNumberElement = numberCell; 348 } 349 350 this._clearStatistics(); 351 352 let bottomDividerElement = bottomOverviewElement.appendChild(document.createElement("div")); 353 bottomDividerElement.classList.add("divider"); 354 355 let sourcesContainerElement = createChartContainer(bottomOverviewElement, WI.UIString("Sources")); 356 sourcesContainerElement.classList.add("stats"); 357 358 this._sourcesTable = sourcesContainerElement.appendChild(document.createElement("table")); 359 this._sourcesRows = []; 360 361 { 362 let {row, headerCell, numberCell, labelCell} = this._createTableRow(this._sourcesTable); 363 headerCell.textContent = WI.UIString("Filter:"); 364 this._sourcesFilterRow = row; 365 this._sourcesFilterRow.hidden = true; 366 this._sourcesFilterNumberElement = numberCell; 367 this._sourcesFilterLabelElement = labelCell; 368 369 let filterClearElement = numberCell.appendChild(document.createElement("span")); 370 filterClearElement.className = "filter-clear"; 371 filterClearElement.textContent = multiplicationSign; 372 filterClearElement.addEventListener("click", (event) => { 373 this._resetSourcesFilters(); 374 this._layoutStatisticsAndSources(); 375 }); 376 } 377 { 378 let {row, headerCell, numberCell, labelCell} = this._createTableRow(this._sourcesTable); 379 headerCell.textContent = WI.UIString("Timers:"); 380 this._timerInstallationsRow = row; 381 this._timerInstallationsNumberElement = numberCell; 382 this._timerInstallationsLabelElement = labelCell; 383 } 384 { 385 let {row, headerCell, numberCell, labelCell} = this._createTableRow(this._sourcesTable); 386 headerCell.textContent = WI.UIString("Event Handlers:"); 387 this._eventHandlersRow = row; 388 this._eventHandlersNumberElement = numberCell; 389 this._eventHandlersLabelElement = labelCell; 390 } 391 { 392 let {row, headerCell, numberCell, labelCell} = this._createTableRow(this._sourcesTable); 393 headerCell.textContent = WI.UIString("Observer Handlers:"); 394 this._observerHandlersRow = row; 395 this._observerHandlersNumberElement = numberCell; 396 this._observerHandlersLabelElement = labelCell; 397 } 398 399 this._clearSources(); 400 315 401 this.element.addEventListener("mousemove", this._handleGraphMouseMove.bind(this)); 316 402 } … … 320 406 if (this.layoutReason === WI.View.LayoutReason.Resize) 321 407 return; 408 409 if (this.layoutReason !== WI.CPUTimelineView.LayoutReason.Internal) 410 this._sectionLimit = CPUTimelineView.defaultSectionLimit; 322 411 323 412 // Always update timeline ruler. … … 346 435 } 347 436 348 let samplingData = this._computeSamplingData(graphStartTime, visibleEndTime); 349 let nonIdleSamplesCount = samplingData.samples.length - samplingData.samplesIdle; 350 if (!nonIdleSamplesCount) { 351 this._breakdownChart.clear(); 352 this._breakdownChart.needsLayout(); 353 this._clearBreakdownLegend(); 354 } else { 355 let percentScript = samplingData.samplesScript / nonIdleSamplesCount; 356 let percentLayout = samplingData.samplesLayout / nonIdleSamplesCount; 357 let percentPaint = samplingData.samplesPaint / nonIdleSamplesCount; 358 let percentStyle = samplingData.samplesStyle / nonIdleSamplesCount; 359 360 this._breakdownLegendScriptElement.textContent = `${Number.percentageString(percentScript)} (${samplingData.samplesScript})`; 361 this._breakdownLegendLayoutElement.textContent = `${Number.percentageString(percentLayout)} (${samplingData.samplesLayout})`; 362 this._breakdownLegendPaintElement.textContent = `${Number.percentageString(percentPaint)} (${samplingData.samplesPaint})`; 363 this._breakdownLegendStyleElement.textContent = `${Number.percentageString(percentStyle)} (${samplingData.samplesStyle})`; 364 365 this._breakdownChart.values = [percentScript * 100, percentLayout * 100, percentPaint * 100, percentStyle * 100]; 366 this._breakdownChart.needsLayout(); 367 368 let centerElement = this._breakdownChart.centerElement; 369 let samplesElement = centerElement.firstChild; 370 if (!samplesElement) { 371 samplesElement = centerElement.appendChild(document.createElement("div")); 372 samplesElement.classList.add("samples"); 373 samplesElement.title = WI.UIString("Time spent on the main thread"); 374 } 375 376 let millisecondsStringNoDecimal = WI.UIString("%.0fms").format(nonIdleSamplesCount); 377 samplesElement.textContent = millisecondsStringNoDecimal; 378 } 437 this._statisticsData = this._computeStatisticsData(graphStartTime, visibleEndTime); 438 this._layoutBreakdownChart(); 439 this._layoutStatisticsAndSources(); 379 440 380 441 let dataPoints = []; … … 638 699 let graphWidth = (graphEndTime - graphStartTime) / secondsPerPixel; 639 700 let size = new WI.Size(graphWidth, CPUTimelineView.indicatorViewHeight); 640 this._mainThreadWorkIndicatorView.updateChart( samplingData.samples, size, visibleEndTime, xScaleIndicatorRange);701 this._mainThreadWorkIndicatorView.updateChart(this._statisticsData.samples, size, visibleEndTime, xScaleIndicatorRange); 641 702 642 703 this._layoutEnergyChart(average, visibleDuration); … … 644 705 645 706 // Private 707 708 _layoutBreakdownChart() 709 { 710 let {samples, samplesScript, samplesLayout, samplesPaint, samplesStyle, samplesIdle} = this._statisticsData; 711 712 let nonIdleSamplesCount = samples.length - samplesIdle; 713 if (!nonIdleSamplesCount) { 714 this._breakdownChart.clear(); 715 this._breakdownChart.needsLayout(); 716 this._clearBreakdownLegend(); 717 return; 718 } 719 720 let percentScript = samplesScript / nonIdleSamplesCount; 721 let percentLayout = samplesLayout / nonIdleSamplesCount; 722 let percentPaint = samplesPaint / nonIdleSamplesCount; 723 let percentStyle = samplesStyle / nonIdleSamplesCount; 724 725 this._breakdownLegendScriptElement.textContent = `${Number.percentageString(percentScript)} (${samplesScript})`; 726 this._breakdownLegendLayoutElement.textContent = `${Number.percentageString(percentLayout)} (${samplesLayout})`; 727 this._breakdownLegendPaintElement.textContent = `${Number.percentageString(percentPaint)} (${samplesPaint})`; 728 this._breakdownLegendStyleElement.textContent = `${Number.percentageString(percentStyle)} (${samplesStyle})`; 729 730 this._breakdownChart.values = [percentScript * 100, percentLayout * 100, percentPaint * 100, percentStyle * 100]; 731 this._breakdownChart.needsLayout(); 732 733 let centerElement = this._breakdownChart.centerElement; 734 let samplesElement = centerElement.firstChild; 735 if (!samplesElement) { 736 samplesElement = centerElement.appendChild(document.createElement("div")); 737 samplesElement.classList.add("samples"); 738 samplesElement.title = WI.UIString("Time spent on the main thread"); 739 } 740 741 let millisecondsStringNoDecimal = WI.UIString("%.0fms").format(nonIdleSamplesCount); 742 samplesElement.textContent = millisecondsStringNoDecimal; 743 } 744 745 _layoutStatisticsAndSources() 746 { 747 this._layoutStatisticsSection(); 748 this._layoutSourcesSection(); 749 } 750 751 _layoutStatisticsSection() 752 { 753 let statistics = this._statisticsData; 754 755 this._clearStatistics(); 756 757 this._scriptEntriesNumberElement.textContent = statistics.scriptEntries; 758 759 let createFilterElement = (type, name) => { 760 let span = document.createElement("span"); 761 span.className = "filter"; 762 span.textContent = name; 763 span.addEventListener("mouseup", (event) => { 764 if (span.classList.contains("active")) 765 this._removeSourcesFilter(type, name); 766 else 767 this._addSourcesFilter(type, name); 768 769 this._layoutStatisticsAndSources(); 770 }); 771 772 span.classList.toggle("active", this._sourcesFilter[type].has(name)); 773 774 return span; 775 }; 776 777 let expandAllSections = () => { 778 this._sectionLimit = Infinity; 779 this._layoutStatisticsAndSources(); 780 }; 781 782 function createEllipsisElement() { 783 let span = document.createElement("span"); 784 span.className = "show-more"; 785 span.role = "button"; 786 span.textContent = ellipsis; 787 span.addEventListener("click", (event) => { 788 expandAllSections(); 789 }); 790 return span; 791 } 792 793 // Sort a Map of key => count values in descending order. 794 function sortMapByEntryCount(map) { 795 let entries = Array.from(map); 796 entries.sort((entryA, entryB) => entryB[1] - entryA[1]); 797 return new Map(entries); 798 } 799 800 if (statistics.timerTypes.size) { 801 let i = 0; 802 let sorted = sortMapByEntryCount(statistics.timerTypes); 803 for (let [timerType, count] of sorted) { 804 let headerValue = i === 0 ? WI.UIString("Timers:") : ""; 805 let timerTypeElement = createFilterElement("timer", timerType); 806 this._insertTableRow(this._statisticsTable, this._statisticsRows, {headerValue, numberValue: count, labelValue: timerTypeElement}); 807 808 if (++i === this._sectionLimit && sorted.size > this._sectionLimit) { 809 this._insertTableRow(this._statisticsTable, this._statisticsRows, {labelValue: createEllipsisElement()}); 810 break; 811 } 812 } 813 } 814 815 if (statistics.eventTypes.size) { 816 let i = 0; 817 let sorted = sortMapByEntryCount(statistics.eventTypes); 818 for (let [eventType, count] of sorted) { 819 let headerValue = i === 0 ? WI.UIString("Events:") : ""; 820 let eventTypeElement = createFilterElement("event", eventType); 821 this._insertTableRow(this._statisticsTable, this._statisticsRows, {headerValue, numberValue: count, labelValue: eventTypeElement}); 822 823 if (++i === this._sectionLimit && sorted.size > this._sectionLimit) { 824 this._insertTableRow(this._statisticsTable, this._statisticsRows, {labelValue: createEllipsisElement()}); 825 break; 826 } 827 } 828 } 829 830 if (statistics.observerTypes.size) { 831 let i = 0; 832 let sorted = sortMapByEntryCount(statistics.observerTypes); 833 for (let [observerType, count] of sorted) { 834 let headerValue = i === 0 ? WI.UIString("Observers:") : ""; 835 let observerTypeElement = createFilterElement("observer", observerType); 836 this._insertTableRow(this._statisticsTable, this._statisticsRows, {headerValue, numberValue: count, labelValue: observerTypeElement}); 837 838 if (++i === this._sectionLimit && sorted.size > this._sectionLimit) { 839 this._insertTableRow(this._statisticsTable, this._statisticsRows, {labelValue: createEllipsisElement()}); 840 break; 841 } 842 } 843 } 844 } 845 846 _layoutSourcesSection() 847 { 848 let statistics = this._statisticsData; 849 850 this._clearSources(); 851 852 const unknownLocationKey = "unknown"; 853 854 function keyForSourceCodeLocation(sourceCodeLocation) { 855 if (!sourceCodeLocation) 856 return unknownLocationKey; 857 858 return sourceCodeLocation.sourceCode.url + ":" + sourceCodeLocation.lineNumber + ":" + sourceCodeLocation.columnNumber; 859 } 860 861 function labelForLocation(key, sourceCodeLocation, functionName) { 862 if (key === unknownLocationKey) { 863 let span = document.createElement("span"); 864 span.className = "unknown"; 865 span.textContent = WI.UIString("Unknown Location"); 866 return span; 867 } 868 869 const options = { 870 nameStyle: WI.SourceCodeLocation.NameStyle.Short, 871 columnStyle: WI.SourceCodeLocation.ColumnStyle.Shown, 872 dontFloat: true, 873 ignoreNetworkTab: true, 874 ignoreSearchTab: true, 875 }; 876 return WI.createSourceCodeLocationLink(sourceCodeLocation, options); 877 } 878 879 let timerFilters = this._sourcesFilter.timer; 880 let eventFilters = this._sourcesFilter.event; 881 let observerFilters = this._sourcesFilter.observer; 882 let hasFilters = (timerFilters.size || eventFilters.size || observerFilters.size); 883 884 let sectionLimit = this._sectionLimit; 885 if (isFinite(sectionLimit) && hasFilters) 886 sectionLimit = CPUTimelineView.defaultSectionLimit * 2; 887 888 let expandAllSections = () => { 889 this._sectionLimit = Infinity; 890 this._layoutStatisticsAndSources(); 891 }; 892 893 function createEllipsisElement() { 894 let span = document.createElement("span"); 895 span.className = "show-more"; 896 span.role = "button"; 897 span.textContent = ellipsis; 898 span.addEventListener("click", (event) => { 899 expandAllSections(); 900 }); 901 return span; 902 } 903 904 let timerMap = new Map; 905 let eventHandlerMap = new Map; 906 let observerCallbackMap = new Map; 907 let seenTimers = new Set; 908 909 if (!hasFilters || timerFilters.size) { 910 // Aggregate timers on the location where the timers were installed. 911 // For repeating timers, this includes the total counts the interval fired in the selected time range. 912 for (let record of statistics.timerInstallationRecords) { 913 if (timerFilters.size) { 914 if (record.eventType === WI.ScriptTimelineRecord.EventType.AnimationFrameRequested && !timerFilters.has("requestAnimationFrame")) 915 continue; 916 if (record.eventType === WI.ScriptTimelineRecord.EventType.TimerInstalled && !timerFilters.has("setTimeout")) 917 continue; 918 } 919 920 let callFrame = record.initiatorCallFrame; 921 let sourceCodeLocation = callFrame ? callFrame.sourceCodeLocation : record.sourceCodeLocation; 922 let functionName = callFrame ? callFrame.functionName : ""; 923 let key = keyForSourceCodeLocation(sourceCodeLocation); 924 let entry = timerMap.getOrInitialize(key, {sourceCodeLocation, functionName, count: 0, repeating: false}); 925 if (record.details) { 926 let timerIdentifier = record.details.timerId; 927 let repeatingEntry = statistics.repeatingTimers.get(timerIdentifier); 928 let count = repeatingEntry ? repeatingEntry.count : 1; 929 entry.count += count; 930 if (record.details.repeating) 931 entry.repeating = true; 932 seenTimers.add(timerIdentifier); 933 } else 934 entry.count += 1; 935 } 936 937 // Aggregate repeating timers where we did not see the installation in the selected time range. 938 // This will use the source code location of where the timer fired, which is better than nothing. 939 if (!hasFilters || timerFilters.has("setTimeout")) { 940 for (let [timerId, repeatingEntry] of statistics.repeatingTimers) { 941 if (seenTimers.has(timerId)) 942 continue; 943 // FIXME: <https://webkit.org/b/195351> Web Inspector: CPU Usage Timeline - better resolution of installation source for repeated timers 944 // We could have a map of all repeating timer installations in the whole recording 945 // so that we can provide a function name for these repeating timers lacking an installation point. 946 let sourceCodeLocation = repeatingEntry.record.sourceCodeLocation; 947 let key = keyForSourceCodeLocation(sourceCodeLocation); 948 let entry = timerMap.getOrInitialize(key, {sourceCodeLocation, count: 0, repeating: false}); 949 entry.count += repeatingEntry.count; 950 entry.repeating = true; 951 } 952 } 953 } 954 955 if (!hasFilters || eventFilters.size) { 956 for (let record of statistics.eventHandlerRecords) { 957 if (eventFilters.size && !eventFilters.has(record.details)) 958 continue; 959 let sourceCodeLocation = record.sourceCodeLocation; 960 let key = keyForSourceCodeLocation(sourceCodeLocation); 961 let entry = eventHandlerMap.getOrInitialize(key, {sourceCodeLocation, count: 0}); 962 entry.count += 1; 963 } 964 } 965 966 if (!hasFilters || observerFilters.size) { 967 for (let record of statistics.observerCallbackRecords) { 968 if (observerFilters.size && !observerFilters.has(record.details)) 969 continue; 970 let sourceCodeLocation = record.sourceCodeLocation; 971 let key = keyForSourceCodeLocation(record.sourceCodeLocation); 972 let entry = observerCallbackMap.getOrInitialize(key, {sourceCodeLocation, count: 0}); 973 entry.count += 1; 974 } 975 } 976 977 const headerValue = ""; 978 979 // Sort a Map of key => {count} objects in descending order. 980 function sortMapByEntryCountProperty(map) { 981 let entries = Array.from(map); 982 entries.sort((entryA, entryB) => entryB[1].count - entryA[1].count); 983 return new Map(entries); 984 } 985 986 if (timerMap.size) { 987 let i = 0; 988 let sorted = sortMapByEntryCountProperty(timerMap); 989 for (let [key, entry] of sorted) { 990 let numberValue = entry.repeating ? WI.UIString("~%s", "Approximate Number", "Approximate count of events").format(entry.count) : entry.count; 991 let sourceCodeLocation = entry.callFrame ? entry.callFrame.sourceCodeLocation : entry.sourceCodeLocation; 992 let labelValue = labelForLocation(key, sourceCodeLocation); 993 let followingRow = this._eventHandlersRow; 994 995 let row; 996 if (i === 0) { 997 row = this._timerInstallationsRow; 998 this._timerInstallationsNumberElement.textContent = numberValue; 999 this._timerInstallationsLabelElement.append(labelValue); 1000 } else 1001 row = this._insertTableRow(this._sourcesTable, this._sourcesRows, {headerValue, numberValue, labelValue, followingRow}); 1002 1003 if (entry.functionName) 1004 row.querySelector(".label").append(` ${enDash} ${entry.functionName}`); 1005 1006 if (++i === sectionLimit && sorted.size > sectionLimit) { 1007 this._insertTableRow(this._sourcesTable, this._sourcesRows, {labelValue: createEllipsisElement(), followingRow}); 1008 break; 1009 } 1010 } 1011 } 1012 1013 if (eventHandlerMap.size) { 1014 let i = 0; 1015 let sorted = sortMapByEntryCountProperty(eventHandlerMap); 1016 for (let [key, entry] of sorted) { 1017 let numberValue = entry.count; 1018 let labelValue = labelForLocation(key, entry.sourceCodeLocation); 1019 let followingRow = this._observerHandlersRow; 1020 1021 if (i === 0) { 1022 this._eventHandlersNumberElement.textContent = numberValue; 1023 this._eventHandlersLabelElement.append(labelValue); 1024 } else 1025 this._insertTableRow(this._sourcesTable, this._sourcesRows, {headerValue, numberValue, labelValue, followingRow}); 1026 1027 if (++i === sectionLimit && sorted.size > sectionLimit) { 1028 this._insertTableRow(this._sourcesTable, this._sourcesRows, {labelValue: createEllipsisElement(), followingRow}); 1029 break; 1030 } 1031 } 1032 } 1033 1034 if (observerCallbackMap.size) { 1035 let i = 0; 1036 let sorted = sortMapByEntryCountProperty(observerCallbackMap); 1037 for (let [key, entry] of sorted) { 1038 let numberValue = entry.count; 1039 let labelValue = labelForLocation(key, entry.sourceCodeLocation); 1040 1041 if (i === 0) { 1042 this._observerHandlersNumberElement.textContent = numberValue; 1043 this._observerHandlersLabelElement.append(labelValue); 1044 } else 1045 this._insertTableRow(this._sourcesTable, this._sourcesRows, {headerValue, numberValue, labelValue}); 1046 1047 if (++i === sectionLimit && sorted.size > sectionLimit) { 1048 this._insertTableRow(this._sourcesTable, this._sourcesRows, {labelValue: createEllipsisElement()}); 1049 break; 1050 } 1051 } 1052 } 1053 } 646 1054 647 1055 _layoutEnergyChart(average, visibleDuration) … … 697 1105 } 698 1106 699 _computeS amplingData(startTime, endTime)1107 _computeStatisticsData(startTime, endTime) 700 1108 { 701 1109 // Compute per-millisecond samples of what the main thread was doing. … … 718 1126 const includeRecordBeforeStart = true; 719 1127 1128 function incrementTypeCount(map, key) { 1129 let entry = map.get(key); 1130 if (entry) 1131 map.set(key, entry + 1); 1132 else 1133 map.set(key, 1); 1134 } 1135 1136 let timerInstallationRecords = []; 1137 let eventHandlerRecords = []; 1138 let observerCallbackRecords = []; 1139 let scriptEntries = 0; 1140 let timerTypes = new Map; 1141 let eventTypes = new Map; 1142 let observerTypes = new Map; 1143 1144 let repeatingTimers = new Map; 1145 let possibleRepeatingTimers = new Set; 1146 720 1147 let scriptTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Script); 721 1148 let scriptRecords = scriptTimeline ? scriptTimeline.recordsInTimeRange(startTime, endTime, includeRecordBeforeStart) : []; 722 1149 scriptRecords = scriptRecords.filter((record) => { 1150 // Return true for event types that define script entries/exits. 1151 // Return false for events with no time ranges or if they are contained in other events. 723 1152 switch (record.eventType) { 724 1153 case WI.ScriptTimelineRecord.EventType.ScriptEvaluated: 725 1154 case WI.ScriptTimelineRecord.EventType.APIScriptEvaluated: 1155 scriptEntries++; 1156 return true; 1157 726 1158 case WI.ScriptTimelineRecord.EventType.ObserverCallback: 1159 incrementTypeCount(observerTypes, record.details); 1160 observerCallbackRecords.push(record); 1161 scriptEntries++; 1162 return true; 1163 727 1164 case WI.ScriptTimelineRecord.EventType.EventDispatched: 1165 incrementTypeCount(eventTypes, record.details); 1166 eventHandlerRecords.push(record); 1167 scriptEntries++; 1168 return true; 1169 728 1170 case WI.ScriptTimelineRecord.EventType.MicrotaskDispatched: 1171 // Do not normally count this as a script entry, but they may have a time range 1172 // that is not covered by script entry (queueMicrotask). 1173 return true; 1174 729 1175 case WI.ScriptTimelineRecord.EventType.TimerFired: 1176 incrementTypeCount(timerTypes, "setTimeout"); 1177 if (possibleRepeatingTimers.has(record.details)) { 1178 let entry = repeatingTimers.get(record.details); 1179 if (entry) 1180 entry.count += 1; 1181 else 1182 repeatingTimers.set(record.details, {record, count: 1}); 1183 } else 1184 possibleRepeatingTimers.add(record.details); 1185 scriptEntries++; 1186 return true; 1187 730 1188 case WI.ScriptTimelineRecord.EventType.AnimationFrameFired: 731 // These event types define script entry/exits. 1189 incrementTypeCount(timerTypes, "requestAnimationFrame"); 1190 scriptEntries++; 732 1191 return true; 733 1192 734 1193 case WI.ScriptTimelineRecord.EventType.AnimationFrameRequested: 1194 case WI.ScriptTimelineRecord.EventType.TimerInstalled: 1195 // These event types have no time range, or are contained by the others. 1196 timerInstallationRecords.push(record); 1197 return false; 1198 735 1199 case WI.ScriptTimelineRecord.EventType.AnimationFrameCanceled: 736 case WI.ScriptTimelineRecord.EventType.TimerInstalled:737 1200 case WI.ScriptTimelineRecord.EventType.TimerRemoved: 738 1201 case WI.ScriptTimelineRecord.EventType.ProbeSampleRecorded: … … 845 1308 samplesPaint, 846 1309 samplesStyle, 1310 scriptEntries, 1311 timerTypes, 1312 eventTypes, 1313 observerTypes, 1314 timerInstallationRecords, 1315 eventHandlerRecords, 1316 observerCallbackRecords, 1317 repeatingTimers, 847 1318 }; 848 1319 } … … 857 1328 858 1329 this._workerViews = []; 1330 } 1331 1332 _resetSourcesFilters() 1333 { 1334 if (!this._sourcesFilter) 1335 return; 1336 1337 this._sourcesFilterRow.hidden = true; 1338 this._sourcesFilterLabelElement.removeChildren(); 1339 1340 this._timerInstallationsRow.hidden = false; 1341 this._eventHandlersRow.hidden = false; 1342 this._observerHandlersRow.hidden = false; 1343 1344 this._sourcesFilter.timer.clear(); 1345 this._sourcesFilter.event.clear(); 1346 this._sourcesFilter.observer.clear(); 1347 } 1348 1349 _addSourcesFilter(type, name) 1350 { 1351 this._sourcesFilter[type].add(name); 1352 this._updateSourcesFilters(); 1353 } 1354 1355 _removeSourcesFilter(type, name) 1356 { 1357 this._sourcesFilter[type].delete(name); 1358 this._updateSourcesFilters(); 1359 } 1360 1361 _updateSourcesFilters() 1362 { 1363 let timerFilters = this._sourcesFilter.timer; 1364 let eventFilters = this._sourcesFilter.event; 1365 let observerFilters = this._sourcesFilter.observer; 1366 1367 if (!timerFilters.size && !eventFilters.size && !observerFilters.size) { 1368 this._resetSourcesFilters(); 1369 return; 1370 } 1371 1372 let createActiveFilterElement = (type, name) => { 1373 let span = document.createElement("span"); 1374 span.className = "filter active"; 1375 span.textContent = name; 1376 span.addEventListener("mouseup", (event) => { 1377 this._removeSourcesFilter(type, name); 1378 this._layoutStatisticsAndSources(); 1379 }); 1380 return span; 1381 } 1382 1383 this._sourcesFilterRow.hidden = false; 1384 this._sourcesFilterLabelElement.removeChildren(); 1385 1386 for (let name of timerFilters) 1387 this._sourcesFilterLabelElement.appendChild(createActiveFilterElement("timer", name)); 1388 for (let name of eventFilters) 1389 this._sourcesFilterLabelElement.appendChild(createActiveFilterElement("event", name)); 1390 for (let name of observerFilters) 1391 this._sourcesFilterLabelElement.appendChild(createActiveFilterElement("observer", name)); 1392 1393 this._timerInstallationsRow.hidden = !timerFilters.size; 1394 this._eventHandlersRow.hidden = !eventFilters.size; 1395 this._observerHandlersRow.hidden = !observerFilters.size; 1396 } 1397 1398 _createTableRow(table) 1399 { 1400 let row = table.appendChild(document.createElement("tr")); 1401 1402 let headerCell = row.appendChild(document.createElement("th")); 1403 1404 let numberCell = row.appendChild(document.createElement("td")); 1405 numberCell.className = "number"; 1406 1407 let labelCell = row.appendChild(document.createElement("td")); 1408 labelCell.className = "label"; 1409 1410 return {row, headerCell, numberCell, labelCell}; 1411 } 1412 1413 _insertTableRow(table, rowList, {headerValue, numberValue, labelValue, followingRow}) 1414 { 1415 let {row, headerCell, numberCell, labelCell} = this._createTableRow(table); 1416 rowList.push(row); 1417 1418 if (followingRow) 1419 table.insertBefore(row, followingRow); 1420 1421 if (headerValue) 1422 headerCell.textContent = headerValue; 1423 1424 if (numberValue) 1425 numberCell.textContent = numberValue; 1426 1427 if (labelValue) 1428 labelCell.append(labelValue); 1429 1430 return row; 1431 } 1432 1433 _clearStatistics() 1434 { 1435 this._scriptEntriesNumberElement.textContent = emDash; 1436 1437 for (let row of this._statisticsRows) 1438 row.remove(); 1439 this._statisticsRows = []; 1440 } 1441 1442 _clearSources() 1443 { 1444 this._timerInstallationsNumberElement.textContent = emDash; 1445 this._timerInstallationsLabelElement.textContent = ""; 1446 1447 this._eventHandlersNumberElement.textContent = emDash; 1448 this._eventHandlersLabelElement.textContent = ""; 1449 1450 this._observerHandlersNumberElement.textContent = emDash; 1451 this._observerHandlersLabelElement.textContent = ""; 1452 1453 for (let row of this._sourcesRows) 1454 row.remove(); 1455 this._sourcesRows = []; 859 1456 } 860 1457 … … 1010 1607 }; 1011 1608 1609 WI.CPUTimelineView.LayoutReason = { 1610 Internal: Symbol("cpu-timeline-view-internal-layout"), 1611 }; 1612 1012 1613 // NOTE: UI follows this order. 1013 1614 WI.CPUTimelineView.SampleType = { -
trunk/Source/WebInspectorUI/UserInterface/Views/Main.css
r242242 r242562 239 239 .resource-link, 240 240 .go-to-link { 241 color: hsl(0, 0%, 33%);241 color: var(--link-text-color); 242 242 text-decoration: underline; 243 243 cursor: pointer; … … 478 478 } 479 479 480 .resource-link,481 .go-to-link {482 color: var(--text-color-secondary);483 }484 485 480 .expand-list-button { 486 481 color: inherit; -
trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css
r242300 r242562 51 51 --background-color-code: white; 52 52 53 /* Gray background with lighter foreground. In dark mode this is lighter. */ 54 --gray-background-color: hsl(0, 0%, 66%); 55 --gray-foreground-color: white; 56 57 --link-text-color: hsl(0, 0%, 33%); 58 53 59 --selected-foreground-color: white; 54 60 --selected-secondary-text-color: hsla(0, 100%, 100%, 0.7); … … 216 222 --background-color-code: hsl(0, 0%, 21%); 217 223 224 --gray-background-color: hsl(0, 0%, 50%); 225 --gray-foreground-color: hsl(0, 0%, 33%); 226 227 --link-text-color: var(--text-color-secondary); 228 218 229 --background-color-alternate: hsla(0, 0%, var(--foreground-lightness), 0.05); 219 230 --background-color-selected: hsla(0, 0%, var(--foreground-lightness), 0.1);
Note:
See TracChangeset
for help on using the changeset viewer.