diff --git a/.circleci/config.yml b/.circleci/config.yml index 2888d8502c..faaedbc8ee 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2.1 executors: node: docker: - - image: cimg/node:16.12 + - image: cimg/node:16.13 base: docker: - image: cimg/base:stable diff --git a/docs-user/_sidebar.md b/docs-user/_sidebar.md index 402658de70..4374d7abae 100644 --- a/docs-user/_sidebar.md +++ b/docs-user/_sidebar.md @@ -5,7 +5,9 @@ * [Profiler Fundamentals](./guide-profiler-fundamentals.md) * [Stack samples and call trees](./guide-stack-samples-and-call-trees.md) * [Filtering call trees](./guide-filtering-call-trees.md) - * [Remote Profiling Firefox for Android](./guide-remote-profiling.md) + * [Profiling Firefox for Android](./guide-profiling-firefox-android.md) + * [Remote Profiling](./guide-remote-profiling.md) + * [Profiling directly on the device](./guide-profiling-android-directly-on-device.md) * [Memory Allocations](./memory-allocations.md) * [Advanced Topics](./advanced-topics.md) * [Profiling Firefox Startup & Shutdown](./guide-startup-shutdown.md) diff --git a/docs-user/guide-profiling-android-directly-on-device.md b/docs-user/guide-profiling-android-directly-on-device.md new file mode 100644 index 0000000000..fccf825c19 --- /dev/null +++ b/docs-user/guide-profiling-android-directly-on-device.md @@ -0,0 +1,34 @@ +# Profiling Firefox for Android directly on device + +The Firefox Profiler can be used without the remote debugging option. It offers a little less flexibility (can't edit the options and the profile is +automatically uploaded). However, it does allow you to capture a profile without the need of a PC. + +## Setup + +### Pick a build to profile +We recommend profiling a Firefox build from any release channel (i.e. not debug), whether downloaded from Google Play, Taskcluster, or built locally. + +### Enable secret settings on the mobile device + +To enable secret settings, follow these steps: + + - Click on the [three dot icon next to the URL bar](./images/about-url.png) + - Select the ["Settings" option](./images/settings-menu.png). + - Scroll to the bottom of the settings page and select the "About Firefox" + - Click the "Firefox" logo 5 times. [A toast should appear at the bottom of your screen with the number of click left before unlocking the secret menu](./images/secret-menu-toast.png). + - Go back to the "Settings" screen and scroll to the bottom where you should see the ["Start Profiler" option](./images/start-profiler.png). + +## Usage instructions + +### To start the profiler + + - Click on "Start Profiler" and you should see a dialogue appear. + - Choose one of the four options that matches the closest to what you're trying to profile. + - Click "Start Profiler" and a toast should appear with the "Profiler started" message. + +### To stop the profiler + + - Go back to the Settings screen + - Scroll to the bottom and you should see a "Stop profiler" option has replaced the "Start Profiler" one. + - After you click it, you should see a dialogue with a warning regarding the information contained in the profile. + - Once stopped, the URL for the profile that finished recording will be copied to your clipboard which you can then use to share. diff --git a/docs-user/guide-profiling-firefox-android.md b/docs-user/guide-profiling-firefox-android.md new file mode 100644 index 0000000000..89cdb27c2e --- /dev/null +++ b/docs-user/guide-profiling-firefox-android.md @@ -0,0 +1,7 @@ +# Profiling Firefox for Android + +Two options are possible: + +* [Remote Profiling Firefox for Android](./guide-remote-profiling.md) provides the most options but can be cumbersome to set up; +* [Profiling Firefox for Android directly on the device](./guide-profiling-android-directly-on-device.md) has fewer options but should be easier to set up. + diff --git a/docs-user/images/about-url.png b/docs-user/images/about-url.png new file mode 100644 index 0000000000..929e72e8ea Binary files /dev/null and b/docs-user/images/about-url.png differ diff --git a/docs-user/images/secret-menu-toast.png b/docs-user/images/secret-menu-toast.png new file mode 100644 index 0000000000..14ba52e4fb Binary files /dev/null and b/docs-user/images/secret-menu-toast.png differ diff --git a/docs-user/images/settings-menu.png b/docs-user/images/settings-menu.png new file mode 100644 index 0000000000..9275fd21df Binary files /dev/null and b/docs-user/images/settings-menu.png differ diff --git a/docs-user/images/start-profiler.png b/docs-user/images/start-profiler.png new file mode 100644 index 0000000000..3d0367089c Binary files /dev/null and b/docs-user/images/start-profiler.png differ diff --git a/locales/el/app.ftl b/locales/el/app.ftl index 5eff84171f..679f7657e7 100644 --- a/locales/el/app.ftl +++ b/locales/el/app.ftl @@ -677,6 +677,21 @@ TrackMemoryGraph--relative-memory-at-this-time = σχετική μνήμη αυ TrackMemoryGraph--memory-range-in-graph = εύρος μνήμης στο γράφημα TrackMemoryGraph--operations-since-the-previous-sample = λειτουργίες από το προηγούμενο δείγμα +## TrackPowerGraph +## This is used to show the power used by the CPU and other chips in a computer, +## graphed over time. +## It's not displayed by default in the UI, but an example can be found at +## https://share.firefox.dev/3a1fiT7. + +# This is used in the tooltip when the power value uses the Watt unit. +# Variables: +# $value (String) - the power value at this location +TrackPowerGraph--tooltip-power-watt = Ισχύς: { $value } W +# This is used in the tooltip when the power value uses the Milliwatt unit. +# Variables: +# $value (String) - the power value at this location +TrackPowerGraph--tooltip-power-milliwatt = Ισχύς: { $value } mW + ## TrackSearchField ## The component that is used for the search input in the track context menu. diff --git a/locales/en-US/app.ftl b/locales/en-US/app.ftl index a8d8eec868..30cf369762 100644 --- a/locales/en-US/app.ftl +++ b/locales/en-US/app.ftl @@ -223,11 +223,6 @@ FooterLinks--hide-button = ## The timeline component of the full view in the analysis UI at the top of the ## page. -FullTimeline--graph-type = Graph type: -FullTimeline--categories-with-cpu = Categories with CPU -FullTimeline--categories = Categories -FullTimeline--stack-height = Stack height - # This string is used as the text of the track selection button. # Displays the ratio of visible tracks count to total tracks count in the timeline. # We have spans here to make the numbers bold. diff --git a/locales/sv-SE/app.ftl b/locales/sv-SE/app.ftl index 12b3962c8d..6a5dc91ff0 100644 --- a/locales/sv-SE/app.ftl +++ b/locales/sv-SE/app.ftl @@ -674,6 +674,21 @@ TrackMemoryGraph--relative-memory-at-this-time = relativa minnet vid denna tidpu TrackMemoryGraph--memory-range-in-graph = minnesintervall i grafen TrackMemoryGraph--operations-since-the-previous-sample = operationer sedan föregående prov +## TrackPowerGraph +## This is used to show the power used by the CPU and other chips in a computer, +## graphed over time. +## It's not displayed by default in the UI, but an example can be found at +## https://share.firefox.dev/3a1fiT7. + +# This is used in the tooltip when the power value uses the Watt unit. +# Variables: +# $value (String) - the power value at this location +TrackPowerGraph--tooltip-power-watt = Effekt: { $value } W +# This is used in the tooltip when the power value uses the Milliwatt unit. +# Variables: +# $value (String) - the power value at this location +TrackPowerGraph--tooltip-power-milliwatt = Effekt: { $value } mW + ## TrackSearchField ## The component that is used for the search input in the track context menu. diff --git a/package.json b/package.json index 0f2e6a82f6..6a22c30fd0 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@codemirror/lang-javascript": "^6.0.1", "@codemirror/lang-rust": "^6.0.0", "@codemirror/language": "^6.2.0", - "@codemirror/state": "^6.0.1", + "@codemirror/state": "^6.1.0", "@codemirror/view": "^6.0.2", "@firefox-devtools/react-contextmenu": "^5.0.0", "@fluent/bundle": "^0.17.1", @@ -65,7 +65,7 @@ "classnames": "^2.3.1", "common-tags": "^1.8.2", "copy-to-clipboard": "^3.1.0", - "core-js": "^3.23.3", + "core-js": "^3.23.5", "escape-string-regexp": "^4.0.0", "gecko-profiler-demangle": "^0.3.3", "idb": "^7.0.2", @@ -79,7 +79,7 @@ "query-string": "^7.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-intersection-observer": "^9.3.4", + "react-intersection-observer": "^9.3.5", "react-redux": "^8.0.2", "react-splitter-layout": "^4.0.0", "react-transition-group": "^4.4.2", @@ -100,7 +100,7 @@ "@babel/preset-env": "^7.18.6", "@babel/preset-flow": "^7.18.6", "@babel/preset-react": "^7.18.6", - "@testing-library/dom": "^8.14.0", + "@testing-library/dom": "^8.16.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "alex": "^10.0.0", @@ -117,12 +117,12 @@ "css-loader": "^5.2.7", "cssnano": "^5.0.2", "devtools-license-check": "^0.9.0", - "eslint": "^8.18.0", + "eslint": "^8.19.0", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^26.5.3", + "eslint-plugin-jest": "^26.6.0", "eslint-plugin-jest-dom": "^4.0.2", "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-prettier": "^4.2.1", @@ -139,7 +139,7 @@ "husky": "^4.3.8", "jest": "^28.1.2", "jest-environment-jsdom": "^28.1.2", - "jest-extended": "^2.0.0", + "jest-extended": "^2.1.0", "json-loader": "^0.5.7", "local-web-server": "^4.2.1", "lockfile-lint": "^4.7.6", diff --git a/src/actions/profile-view.js b/src/actions/profile-view.js index 7eb2b6de08..64e160a562 100644 --- a/src/actions/profile-view.js +++ b/src/actions/profile-view.js @@ -349,9 +349,7 @@ export function selectTrack( case 'memory': { const { counterIndex } = localTrack; const counterSelectors = getCounterSelectors(counterIndex); - const counter = counterSelectors.getCommittedRangeFilteredCounter( - getState() - ); + const counter = counterSelectors.getCounter(getState()); selectedThreadIndex = counter.mainThreadIndex; break; } @@ -363,9 +361,7 @@ export function selectTrack( case 'power': { const { counterIndex } = localTrack; const counterSelectors = getCounterSelectors(counterIndex); - const counter = counterSelectors.getCommittedRangeFilteredCounter( - getState() - ); + const counter = counterSelectors.getCounter(getState()); selectedThreadIndex = counter.mainThreadIndex; break; } @@ -384,14 +380,13 @@ export function selectTrack( ); } - const doesNextTrackHaveSelectedTab = getThreadSelectors(selectedThreadIndex) - .getUsefulTabs(getState()) - .includes(selectedTab); - - if (!doesNextTrackHaveSelectedTab) { + const visibleTabs = getThreadSelectors(selectedThreadIndex).getUsefulTabs( + getState() + ); + if (!visibleTabs.includes(selectedTab)) { // If the user switches to another track that doesn't have the current - // selectedTab then switch to the calltree. - selectedTab = 'calltree'; + // selectedTab then switch to the first tab. + selectedTab = visibleTabs[0]; } let selectedThreadIndexes = new Set(getSelectedThreadIndexes(getState())); @@ -550,16 +545,13 @@ export function selectActiveTabTrack( ); } - const doesNextTrackHaveSelectedTab = getThreadSelectors( - selectedThreadIndexes - ) - .getUsefulTabs(getState()) - .includes(selectedTab); - - if (!doesNextTrackHaveSelectedTab) { + const visibleTabs = getThreadSelectors(selectedThreadIndexes).getUsefulTabs( + getState() + ); + if (!visibleTabs.includes(selectedTab)) { // If the user switches to another track that doesn't have the current - // selectedTab then switch to the calltree. - selectedTab = 'calltree'; + // selectedTab then switch to the first tab. + selectedTab = visibleTabs[0]; } if ( diff --git a/src/actions/receive-profile.js b/src/actions/receive-profile.js index 744ccfb02f..46b985bbc3 100644 --- a/src/actions/receive-profile.js +++ b/src/actions/receive-profile.js @@ -33,7 +33,6 @@ import { getProfile, getView, getRelevantPagesForActiveTab, - getIsCPUUtilizationProvided, getSymbolServerUrl, getActiveTabID, } from 'firefox-profiler/selectors'; @@ -58,6 +57,7 @@ import { computeActiveTabTracks } from 'firefox-profiler/profile-logic/active-ta import { setDataSource } from './profile-view'; import { fatalError } from './errors'; import { GOOGLE_STORAGE_BUCKET } from 'firefox-profiler/app-logic/constants'; +import { determineTimelineType } from 'firefox-profiler/profile-logic/profile-data'; import type { RequestedLib, @@ -335,20 +335,9 @@ export function finalizeFullProfileView( profile ); - // Check the profile to see if we have threadCPUDelta values and switch to - // the category view with CPU if we have. This is needed only while we are - // still experimenting with the new activity graph. We should remove this - // when we have this on by default. let timelineType = null; - if ( - !hasUrlInfo && - profile.meta.sampleUnits && - profile.threads.some((thread) => thread.samples.threadCPUDelta) - ) { - const hasCPUDeltaValues = getIsCPUUtilizationProvided(getState()); - if (hasCPUDeltaValues) { - timelineType = 'cpu-category'; - } + if (!hasUrlInfo) { + timelineType = determineTimelineType(profile); } withHistoryReplaceStateSync(() => { @@ -588,15 +577,8 @@ export function finalizeActiveTabProfileView( // still experimenting with the new activity graph. We should remove this // when we have this on by default. let timelineType = null; - if ( - !hasUrlInfo && - profile.meta.sampleUnits && - profile.threads[0].samples.threadCPUDelta - ) { - const hasCPUDeltaValues = getIsCPUUtilizationProvided(getState()); - if (hasCPUDeltaValues) { - timelineType = 'cpu-category'; - } + if (!hasUrlInfo) { + timelineType = determineTimelineType(profile); } dispatch({ diff --git a/src/app-logic/constants.js b/src/app-logic/constants.js index 3302c0926a..a792e7dbbf 100644 --- a/src/app-logic/constants.js +++ b/src/app-logic/constants.js @@ -18,8 +18,6 @@ export const TIMELINE_MARGIN_RIGHT = 15; export const TIMELINE_MARGIN_LEFT = 150; export const ACTIVE_TAB_TIMELINE_MARGIN_LEFT = 0; -export const TIMELINE_SETTINGS_HEIGHT = 26; - // Export the value for tests, and for computing the max height of the timeline // for the splitter. export const FULL_TRACK_SCREENSHOT_HEIGHT = 50; diff --git a/src/app-logic/tabs-handling.js b/src/app-logic/tabs-handling.js index 936dd44805..28850d0312 100644 --- a/src/app-logic/tabs-handling.js +++ b/src/app-logic/tabs-handling.js @@ -40,3 +40,9 @@ export const tabsWithTitleL10nIdArray: $ReadOnlyArray = name: tabSlug, title: tabsWithTitleL10nId[tabSlug], })); + +export const tabsShowingSampleData: $ReadOnlyArray = [ + 'calltree', + 'flame-graph', + 'stack-chart', +]; diff --git a/src/app-logic/url-handling.js b/src/app-logic/url-handling.js index 4b3c52979c..00e434f6f6 100644 --- a/src/app-logic/url-handling.js +++ b/src/app-logic/url-handling.js @@ -46,7 +46,7 @@ import { } from '../utils/uintarray-encoding'; import { tabSlugs } from '../app-logic/tabs-handling'; -export const CURRENT_URL_VERSION = 6; +export const CURRENT_URL_VERSION = 7; /** * This static piece of state might look like an anti-pattern, but it's a relatively @@ -366,10 +366,9 @@ export function getQueryStringFromUrlState(urlState: UrlState): string { ? undefined : urlState.profileSpecific.implementation, timelineType: - // The default is the category view, so only add it to the URL if it's the - // stack or cpu-category view. - // TODO: We should make the 'cpu-category' the new default with an upgrader. - urlState.profileSpecific.timelineType === 'category' + // The default is the cpu-category view, so only add it to the URL if it's + // the stack or category view. + urlState.profileSpecific.timelineType === 'cpu-category' ? undefined : urlState.profileSpecific.timelineType, }: BaseQueryShape); @@ -1088,6 +1087,26 @@ const _upgraders: {| delete query.transforms; } }, + [7]: ({ query }: ProcessedLocationBeforeUpgrade) => { + // Default timeline type has been changed to 'cpu-category' from 'category'. + // Default timeline type isn't needed to be in the url, revert the values in + // the query to reflect that. + switch (query.timelineType) { + case 'cpu-category': + // This is the default value now. It's not needed in the url. + delete query.timelineType; + break; + case 'stack': + // Do nothing for this. + break; + case 'category': + default: + // We can either have 'category' or nothing for this value. We should + // explicitly add for this case. + query.timelineType = 'category'; + break; + } + }, }; for (let destVersion = 1; destVersion <= CURRENT_URL_VERSION; destVersion++) { @@ -1213,6 +1232,6 @@ function validateTimelineType(type: ?string): TimelineType { default: // Type assert we've checked everything: (timelineType: empty); - return 'category'; + return 'cpu-category'; } } diff --git a/src/components/shared/SampleTooltipContents.js b/src/components/shared/SampleTooltipContents.js index 5ed9c76bc8..29d2f32fc6 100644 --- a/src/components/shared/SampleTooltipContents.js +++ b/src/components/shared/SampleTooltipContents.js @@ -7,7 +7,11 @@ import * as React from 'react'; import { Backtrace } from './Backtrace'; import { TooltipDetailSeparator } from '../tooltip/TooltipDetails'; -import { getCategoryPairLabel } from 'firefox-profiler/profile-logic/profile-data'; +import { + getCategoryPairLabel, + getFuncNamesAndOriginsForPath, + convertStackToCallNodeAndCategoryPath, +} from 'firefox-profiler/profile-logic/profile-data'; import { formatMilliseconds, formatPercent, @@ -20,6 +24,7 @@ import type { Thread, } from 'firefox-profiler/types'; import type { CpuRatioInTimeRange } from './thread/ActivityGraphFills'; +import { ensureExists } from '../../utils/flow'; type CPUProps = CpuRatioInTimeRange; @@ -52,7 +57,6 @@ class SampleTooltipCPUContents extends React.PureComponent {
CPU:
{cpuUsageAndPercentage}
-
); } @@ -117,6 +121,22 @@ export class SampleTooltipContents extends React.PureComponent { categories, implementationFilter, } = this.props; + + const { samples, stackTable } = rangeFilteredThread; + const stackIndex = samples.stack[sampleIndex]; + const hasSamples = samples.length > 0 && stackTable.length > 1; + let hasStack = false; + if (hasSamples) { + const stack = getFuncNamesAndOriginsForPath( + convertStackToCallNodeAndCategoryPath( + rangeFilteredThread, + ensureExists(stackIndex) + ), + rangeFilteredThread + ); + hasStack = stack.length > 1 || stack[0].funcName !== '(root)'; + } + return ( <> {cpuRatioInTimeRange === null ? null : ( @@ -125,12 +145,17 @@ export class SampleTooltipContents extends React.PureComponent { timeRange={cpuRatioInTimeRange.timeRange} /> )} - + {hasStack && cpuRatioInTimeRange !== null ? ( + + ) : null} + {!hasStack ? null : ( + + )} ); } diff --git a/src/components/shared/thread/ActivityGraph.js b/src/components/shared/thread/ActivityGraph.js index 1e65616e48..e9ab1cd886 100644 --- a/src/components/shared/thread/ActivityGraph.js +++ b/src/components/shared/thread/ActivityGraph.js @@ -26,6 +26,7 @@ import type { SelectedState, Milliseconds, CssPixels, + TimelineType, } from 'firefox-profiler/types'; import type { ActivityFillGraphQuerier, @@ -54,6 +55,7 @@ export type Props = {| +enableCPUUsage: boolean, +maxThreadCPUDeltaPerMs: number, +implementationFilter: ImplementationFilter, + +timelineType: TimelineType, ...SizeProps, |}; @@ -169,6 +171,7 @@ class ThreadActivityGraphImpl extends React.PureComponent { implementationFilter, width, height, + timelineType, } = this.props; const { hoveredPixelState, mouseX, mouseY } = this.state; return ( @@ -204,7 +207,11 @@ class ThreadActivityGraphImpl extends React.PureComponent { { - _changeToCategories = () => this.props.changeTimelineType('category'); - _changeToCPUCategories = () => this.props.changeTimelineType('cpu-category'); - _changeToStacks = () => this.props.changeTimelineType('stack'); - - render() { - const { timelineType, isCPUUtilizationProvided } = this.props; - - return ( -
-
- Graph type: - {isCPUUtilizationProvided ? ( - - ) : null} - - -
-
- ); - } -} - class TimelineSettingsHiddenTracks extends React.PureComponent<{| +hiddenTrackCount: HiddenTrackCount, +changeRightClickedTrack: typeof changeRightClickedTrack, @@ -195,50 +119,6 @@ class TimelineSettingsHiddenTracks extends React.PureComponent<{| } } -class TimelineSettingsActiveTabView extends React.PureComponent<{| - +activeTabID: TabID | null, - +timelineTrackOrganization: TimelineTrackOrganization, - +changeTimelineTrackOrganization: typeof changeTimelineTrackOrganization, -|}> { - _toggleActiveTabView = () => { - const { - timelineTrackOrganization, - changeTimelineTrackOrganization, - activeTabID, - } = this.props; - if (timelineTrackOrganization.type === 'full' && activeTabID !== null) { - changeTimelineTrackOrganization({ - type: 'active-tab', - tabID: activeTabID, - }); - } else { - changeTimelineTrackOrganization({ type: 'full' }); - } - }; - - render() { - const { activeTabID, timelineTrackOrganization } = this.props; - if (activeTabID === null) { - return null; - } - - return ( -
- -
- ); - } -} - class FullTimelineImpl extends React.PureComponent { state = { initialSelected: null, @@ -262,46 +142,13 @@ class FullTimelineImpl extends React.PureComponent { width, globalTrackReferences, panelLayoutGeneration, - timelineType, hiddenTrackCount, - changeTimelineType, changeRightClickedTrack, - activeTabID, - timelineTrackOrganization, - changeTimelineTrackOrganization, - isCPUUtilizationProvided, } = this.props; - // Do not include the left and right margins when computing the timeline width. - const timelineWidth = width - TIMELINE_MARGIN_LEFT - TIMELINE_MARGIN_RIGHT; - return ( <> -
- - {/* - Removing the active tab view checkbox for now. - TODO: Bring it back once we are done with the new active tab UI implementation. - */} - {/* eslint-disable-next-line no-constant-condition */} - {true ? null : ( - - )} -
- +
{ zeroAt={zeroAt} rangeStart={committedRange.start} rangeEnd={committedRange.end} - width={timelineWidth} + width={width} />
({ committedRange: getCommittedRange(state), zeroAt: getZeroAt(state), panelLayoutGeneration: getPanelLayoutGeneration(state), - timelineType: getTimelineType(state), hiddenTrackCount: getHiddenTrackCount(state), - activeTabID: getActiveTabID(state), - timelineTrackOrganization: getTimelineTrackOrganization(state), - isCPUUtilizationProvided: getIsCPUUtilizationProvided(state), }), mapDispatchToProps: { changeGlobalTrackOrder, - changeTimelineType, changeRightClickedTrack, - changeTimelineTrackOrganization, }, component: withSize(FullTimelineImpl), }); diff --git a/src/components/timeline/LocalTrack.js b/src/components/timeline/LocalTrack.js index bef5ba8604..c83db9eb98 100644 --- a/src/components/timeline/LocalTrack.js +++ b/src/components/timeline/LocalTrack.js @@ -179,8 +179,7 @@ export const TimelineLocalTrack = explicitConnect< const selectors = getThreadSelectors(threadIndex); isSelected = selectedThreadIndexes.has(threadIndex) && - selectedTab !== 'network-chart' && - selectedTab !== 'event-delay'; + selectedTab !== 'network-chart'; titleText = selectors.getThreadProcessDetails(state); break; } @@ -209,11 +208,8 @@ export const TimelineLocalTrack = explicitConnect< case 'event-delay': { // Look up the thread information for the process if it exists. const threadIndex = localTrack.threadIndex; - const selectedTab = getSelectedTab(state); const selectors = getThreadSelectors(threadIndex); - isSelected = - threadIndex === selectedThreadIndexes.has(threadIndex) && - selectedTab !== 'event-delay'; + isSelected = threadIndex === selectedThreadIndexes.has(threadIndex); titleText = 'Event Delay of ' + selectors.getThreadProcessDetails(state); break; diff --git a/src/components/timeline/TrackMemory.js b/src/components/timeline/TrackMemory.js index db05561a0a..a31187716f 100644 --- a/src/components/timeline/TrackMemory.js +++ b/src/components/timeline/TrackMemory.js @@ -90,7 +90,7 @@ export const TrackMemory = explicitConnect( mapStateToProps: (state, ownProps) => { const { counterIndex } = ownProps; const counterSelectors = getCounterSelectors(counterIndex); - const counter = counterSelectors.getCommittedRangeFilteredCounter(state); + const counter = counterSelectors.getCounter(state); const { start, end } = getCommittedRange(state); return { threadIndex: counter.mainThreadIndex, diff --git a/src/components/timeline/TrackMemoryGraph.js b/src/components/timeline/TrackMemoryGraph.js index 05f2753bf6..2520deb43f 100644 --- a/src/components/timeline/TrackMemoryGraph.js +++ b/src/components/timeline/TrackMemoryGraph.js @@ -33,6 +33,7 @@ import type { Milliseconds, CssPixels, StartEndRange, + IndexIntoSamplesTable, } from 'firefox-profiler/types'; import type { SizeProps } from 'firefox-profiler/components/shared/WithSize'; @@ -47,6 +48,7 @@ type CanvasProps = {| +rangeStart: Milliseconds, +rangeEnd: Milliseconds, +counter: Counter, + +counterSampleRanges: Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]>, +accumulatedSamples: AccumulatedCounterSamples[], +interval: Milliseconds, +width: CssPixels, @@ -77,6 +79,7 @@ class TrackMemoryCanvas extends React.PureComponent { lineWidth, interval, accumulatedSamples, + counterSampleRanges, } = this.props; if (width === 0) { // This is attempting to draw before the canvas was laid out. @@ -100,7 +103,7 @@ class TrackMemoryCanvas extends React.PureComponent { ctx.clearRect(0, 0, deviceWidth, deviceHeight); const sampleGroups = counter.sampleGroups; - if (sampleGroups.length === 0) { + if (sampleGroups.length === 0 || counterSampleRanges.length === 0) { // Gecko failed to capture samples for some reason and it shouldn't happen for // malloc counter. Print an error and do not draw anything. throw new Error('No sample group found for memory counter'); @@ -121,6 +124,7 @@ class TrackMemoryCanvas extends React.PureComponent { throw new Error('No accumulated sample found for memory counter'); } const { minCount, countRange, accumulatedCounts } = accumulatedSamples[0]; + const [sampleStart, sampleEnd] = counterSampleRanges[0]; { // Draw the chart. @@ -142,7 +146,7 @@ class TrackMemoryCanvas extends React.PureComponent { let x = 0; let y = 0; let firstX = 0; - for (let i = 0; i < samples.length; i++) { + for (let i = sampleStart; i < sampleEnd; i++) { // Create a path for the top of the chart. This is the line that will have // a stroke applied to it. x = (samples.time[i] - rangeStart) * millisecondWidth; @@ -253,6 +257,7 @@ type StateProps = {| +rangeStart: Milliseconds, +rangeEnd: Milliseconds, +counter: Counter, + +counterSampleRanges: Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]>, +accumulatedSamples: AccumulatedCounterSamples[], +interval: Milliseconds, +filteredThread: Thread, @@ -297,7 +302,14 @@ class TrackMemoryGraphImpl extends React.PureComponent { const { pageX: mouseX, pageY: mouseY } = event; // Get the offset from here, and apply it to the time lookup. const { left } = event.currentTarget.getBoundingClientRect(); - const { width, rangeStart, rangeEnd, counter, interval } = this.props; + const { + width, + rangeStart, + rangeEnd, + counter, + interval, + counterSampleRanges, + } = this.props; const rangeLength = rangeEnd - rangeStart; const timeAtMouse = rangeStart + ((mouseX - left) / width) * rangeLength; @@ -317,7 +329,13 @@ class TrackMemoryGraphImpl extends React.PureComponent { } else { // When the mouse pointer hovers between two points, select the point that's closer. let hoveredCounter; - const bisectionCounter = bisectionRight(samples.time, timeAtMouse); + const [sampleStart, sampleEnd] = counterSampleRanges[0]; + const bisectionCounter = bisectionRight( + samples.time, + timeAtMouse, + sampleStart, + sampleEnd + ); if (bisectionCounter > 0 && bisectionCounter < samples.time.length) { const leftDistance = timeAtMouse - samples.time[bisectionCounter - 1]; const rightDistance = samples.time[bisectionCounter] - timeAtMouse; @@ -464,6 +482,7 @@ class TrackMemoryGraphImpl extends React.PureComponent { rangeEnd, unfilteredSamplesRange, counter, + counterSampleRanges, graphHeight, width, lineWidth, @@ -480,6 +499,7 @@ class TrackMemoryGraphImpl extends React.PureComponent { rangeStart={rangeStart} rangeEnd={rangeEnd} counter={counter} + counterSampleRanges={counterSampleRanges} height={graphHeight} width={width} lineWidth={lineWidth} @@ -512,8 +532,10 @@ export const TrackMemoryGraph = explicitConnect< mapStateToProps: (state, ownProps) => { const { counterIndex } = ownProps; const counterSelectors = getCounterSelectors(counterIndex); - const counter = counterSelectors.getCommittedRangeFilteredCounter(state); + const counter = counterSelectors.getCounter(state); const { start, end } = getCommittedRange(state); + const counterSampleRanges = + counterSelectors.getCommittedRangeCounterSampleRanges(state); const selectors = getThreadSelectors(counter.mainThreadIndex); return { counter, @@ -521,6 +543,7 @@ export const TrackMemoryGraph = explicitConnect< accumulatedSamples: counterSelectors.getAccumulateCounterSamples(state), rangeStart: start, rangeEnd: end, + counterSampleRanges, interval: getProfileInterval(state), filteredThread: selectors.getFilteredThread(state), unfilteredSamplesRange: selectors.unfilteredSamplesRange(state), diff --git a/src/components/timeline/TrackPower.js b/src/components/timeline/TrackPower.js index 1cc1ce7109..454b2daad6 100644 --- a/src/components/timeline/TrackPower.js +++ b/src/components/timeline/TrackPower.js @@ -70,7 +70,7 @@ export const TrackPower = explicitConnect({ mapStateToProps: (state, ownProps) => { const { counterIndex } = ownProps; const counterSelectors = getCounterSelectors(counterIndex); - const counter = counterSelectors.getCommittedRangeFilteredCounter(state); + const counter = counterSelectors.getCounter(state); const { start, end } = getCommittedRange(state); return { threadIndex: counter.mainThreadIndex, diff --git a/src/components/timeline/TrackPowerGraph.js b/src/components/timeline/TrackPowerGraph.js index a6acb8d673..8d740143fe 100644 --- a/src/components/timeline/TrackPowerGraph.js +++ b/src/components/timeline/TrackPowerGraph.js @@ -28,6 +28,7 @@ import type { Milliseconds, CssPixels, StartEndRange, + IndexIntoSamplesTable, } from 'firefox-profiler/types'; import type { SizeProps } from 'firefox-profiler/components/shared/WithSize'; @@ -42,6 +43,7 @@ type CanvasProps = {| +rangeStart: Milliseconds, +rangeEnd: Milliseconds, +counter: Counter, + +counterSampleRanges: Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]>, +maxCounterSampleCountsPerMs: number[], +interval: Milliseconds, +width: CssPixels, @@ -68,6 +70,7 @@ class TrackPowerCanvas extends React.PureComponent { lineWidth, interval, maxCounterSampleCountsPerMs, + counterSampleRanges, } = this.props; if (width === 0) { // This is attempting to draw before the canvas was laid out. @@ -91,7 +94,7 @@ class TrackPowerCanvas extends React.PureComponent { ctx.clearRect(0, 0, deviceWidth, deviceHeight); const sampleGroups = counter.sampleGroups; - if (sampleGroups.length === 0) { + if (sampleGroups.length === 0 || counterSampleRanges.length === 0) { // Gecko failed to capture samples for some reason and it shouldn't happen for // malloc counter. Print an error and do not draw anything. throw new Error('No sample group found for power counter'); @@ -103,6 +106,7 @@ class TrackPowerCanvas extends React.PureComponent { return; } + const [sampleStart, sampleEnd] = counterSampleRanges[0]; const countRangePerMs = maxCounterSampleCountsPerMs[0]; { @@ -125,7 +129,7 @@ class TrackPowerCanvas extends React.PureComponent { let x = 0; let y = 0; let firstX = 0; - for (let i = 0; i < samples.length; i++) { + for (let i = sampleStart; i < sampleEnd; i++) { // Create a path for the top of the chart. This is the line that will have // a stroke applied to it. x = (samples.time[i] - rangeStart) * millisecondWidth; @@ -208,6 +212,7 @@ type StateProps = {| +rangeStart: Milliseconds, +rangeEnd: Milliseconds, +counter: Counter, + +counterSampleRanges: Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]>, +maxCounterSampleCountsPerMs: number[], +interval: Milliseconds, +filteredThread: Thread, @@ -246,7 +251,14 @@ class TrackPowerGraphImpl extends React.PureComponent { const { pageX: mouseX, pageY: mouseY } = event; // Get the offset from here, and apply it to the time lookup. const { left } = event.currentTarget.getBoundingClientRect(); - const { width, rangeStart, rangeEnd, counter, interval } = this.props; + const { + width, + rangeStart, + rangeEnd, + counter, + interval, + counterSampleRanges, + } = this.props; const rangeLength = rangeEnd - rangeStart; const timeAtMouse = rangeStart + ((mouseX - left) / width) * rangeLength; @@ -266,7 +278,13 @@ class TrackPowerGraphImpl extends React.PureComponent { } else { // When the mouse pointer hovers between two points, select the point that's closer. let hoveredCounter; - const bisectionCounter = bisectionRight(samples.time, timeAtMouse); + const [sampleStart, sampleEnd] = counterSampleRanges[0]; + const bisectionCounter = bisectionRight( + samples.time, + timeAtMouse, + sampleStart, + sampleEnd + ); if (bisectionCounter > 0 && bisectionCounter < samples.time.length) { const leftDistance = timeAtMouse - samples.time[bisectionCounter - 1]; const rightDistance = samples.time[bisectionCounter] - timeAtMouse; @@ -418,6 +436,7 @@ class TrackPowerGraphImpl extends React.PureComponent { rangeEnd, unfilteredSamplesRange, counter, + counterSampleRanges, graphHeight, width, lineWidth, @@ -434,6 +453,7 @@ class TrackPowerGraphImpl extends React.PureComponent { rangeStart={rangeStart} rangeEnd={rangeEnd} counter={counter} + counterSampleRanges={counterSampleRanges} height={graphHeight} width={width} lineWidth={lineWidth} @@ -466,8 +486,11 @@ export const TrackPowerGraph = explicitConnect< mapStateToProps: (state, ownProps) => { const { counterIndex } = ownProps; const counterSelectors = getCounterSelectors(counterIndex); - const counter = counterSelectors.getCommittedRangeFilteredCounter(state); + const counter = counterSelectors.getCounter(state); const { start, end } = getCommittedRange(state); + const counterSampleRanges = + counterSelectors.getCommittedRangeCounterSampleRanges(state); + const selectors = getThreadSelectors(counter.mainThreadIndex); return { counter, @@ -476,6 +499,7 @@ export const TrackPowerGraph = explicitConnect< counterSelectors.getMaxRangeCounterSampleCountsPerMs(state), rangeStart: start, rangeEnd: end, + counterSampleRanges, interval: getProfileInterval(state), filteredThread: selectors.getFilteredThread(state), unfilteredSamplesRange: selectors.unfilteredSamplesRange(state), diff --git a/src/components/timeline/TrackProcessCPU.js b/src/components/timeline/TrackProcessCPU.js index 68ea4afce4..40923d5286 100644 --- a/src/components/timeline/TrackProcessCPU.js +++ b/src/components/timeline/TrackProcessCPU.js @@ -45,7 +45,7 @@ type Props = ConnectedProps; type State = {||}; -export class TracProcessCPUImpl extends React.PureComponent { +export class TrackProcessCPUImpl extends React.PureComponent { render() { const { counterIndex } = this.props; return ( @@ -74,7 +74,7 @@ export const TrackProcessCPU = explicitConnect< mapStateToProps: (state, ownProps) => { const { counterIndex } = ownProps; const counterSelectors = getCounterSelectors(counterIndex); - const counter = counterSelectors.getCommittedRangeFilteredCounter(state); + const counter = counterSelectors.getCounter(state); const { start, end } = getCommittedRange(state); return { threadIndex: counter.mainThreadIndex, @@ -83,5 +83,5 @@ export const TrackProcessCPU = explicitConnect< }; }, mapDispatchToProps: { updatePreviewSelection }, - component: TracProcessCPUImpl, + component: TrackProcessCPUImpl, }); diff --git a/src/components/timeline/TrackProcessCPUGraph.js b/src/components/timeline/TrackProcessCPUGraph.js index 121a7d2233..9723f1a1fb 100644 --- a/src/components/timeline/TrackProcessCPUGraph.js +++ b/src/components/timeline/TrackProcessCPUGraph.js @@ -27,6 +27,7 @@ import type { Milliseconds, CssPixels, StartEndRange, + IndexIntoSamplesTable, } from 'firefox-profiler/types'; import type { SizeProps } from 'firefox-profiler/components/shared/WithSize'; @@ -41,6 +42,7 @@ type CanvasProps = {| +rangeStart: Milliseconds, +rangeEnd: Milliseconds, +counter: Counter, + +counterSampleRanges: Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]>, +maxCounterSampleCountsPerMs: number[], +interval: Milliseconds, +width: CssPixels, @@ -67,6 +69,7 @@ class TrackProcessCPUCanvas extends React.PureComponent { lineWidth, interval, maxCounterSampleCountsPerMs, + counterSampleRanges, } = this.props; if (width === 0) { // This is attempting to draw before the canvas was laid out. @@ -90,7 +93,7 @@ class TrackProcessCPUCanvas extends React.PureComponent { ctx.clearRect(0, 0, deviceWidth, deviceHeight); const sampleGroups = counter.sampleGroups; - if (sampleGroups.length === 0) { + if (sampleGroups.length === 0 || counterSampleRanges.length === 0) { // Gecko failed to capture samples for some reason and it shouldn't happen for // malloc counter. Print an error and do not draw anything. throw new Error('No sample group found for process CPU counter'); @@ -102,6 +105,7 @@ class TrackProcessCPUCanvas extends React.PureComponent { return; } + const [sampleStart, sampleEnd] = counterSampleRanges[0]; const countRangePerMs = maxCounterSampleCountsPerMs[0]; { @@ -124,7 +128,7 @@ class TrackProcessCPUCanvas extends React.PureComponent { let x = 0; let y = 0; let firstX = 0; - for (let i = 0; i < samples.length; i++) { + for (let i = sampleStart; i < sampleEnd; i++) { // Create a path for the top of the chart. This is the line that will have // a stroke applied to it. x = (samples.time[i] - rangeStart) * millisecondWidth; @@ -210,6 +214,7 @@ type StateProps = {| +rangeStart: Milliseconds, +rangeEnd: Milliseconds, +counter: Counter, + +counterSampleRanges: Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]>, +maxCounterSampleCountsPerMs: number[], +interval: Milliseconds, +filteredThread: Thread, @@ -248,7 +253,14 @@ class TrackProcessCPUGraphImpl extends React.PureComponent { const { pageX: mouseX, pageY: mouseY } = event; // Get the offset from here, and apply it to the time lookup. const { left } = event.currentTarget.getBoundingClientRect(); - const { width, rangeStart, rangeEnd, counter, interval } = this.props; + const { + width, + rangeStart, + rangeEnd, + counter, + interval, + counterSampleRanges, + } = this.props; const rangeLength = rangeEnd - rangeStart; const timeAtMouse = rangeStart + ((mouseX - left) / width) * rangeLength; @@ -268,7 +280,13 @@ class TrackProcessCPUGraphImpl extends React.PureComponent { } else { // When the mouse pointer hovers between two points, select the point that's closer. let hoveredCounter; - const bisectionCounter = bisectionRight(samples.time, timeAtMouse); + const [sampleStart, sampleEnd] = counterSampleRanges[0]; + const bisectionCounter = bisectionRight( + samples.time, + timeAtMouse, + sampleStart, + sampleEnd + ); if (bisectionCounter > 0 && bisectionCounter < samples.time.length) { const leftDistance = timeAtMouse - samples.time[bisectionCounter - 1]; const rightDistance = samples.time[bisectionCounter] - timeAtMouse; @@ -409,6 +427,7 @@ class TrackProcessCPUGraphImpl extends React.PureComponent { rangeEnd, unfilteredSamplesRange, counter, + counterSampleRanges, graphHeight, width, lineWidth, @@ -425,6 +444,7 @@ class TrackProcessCPUGraphImpl extends React.PureComponent { rangeStart={rangeStart} rangeEnd={rangeEnd} counter={counter} + counterSampleRanges={counterSampleRanges} height={graphHeight} width={width} lineWidth={lineWidth} @@ -457,8 +477,10 @@ export const TrackProcessCPUGraph = explicitConnect< mapStateToProps: (state, ownProps) => { const { counterIndex } = ownProps; const counterSelectors = getCounterSelectors(counterIndex); - const counter = counterSelectors.getCommittedRangeFilteredCounter(state); + const counter = counterSelectors.getCounter(state); const { start, end } = getCommittedRange(state); + const counterSampleRanges = + counterSelectors.getCommittedRangeCounterSampleRanges(state); const selectors = getThreadSelectors(counter.mainThreadIndex); return { counter, @@ -467,6 +489,7 @@ export const TrackProcessCPUGraph = explicitConnect< counterSelectors.getMaxCounterSampleCountsPerMs(state), rangeStart: start, rangeEnd: end, + counterSampleRanges, interval: getProfileInterval(state), filteredThread: selectors.getFilteredThread(state), unfilteredSamplesRange: selectors.unfilteredSamplesRange(state), diff --git a/src/components/timeline/TrackThread.js b/src/components/timeline/TrackThread.js index 94e54a1c09..bbeb90c410 100644 --- a/src/components/timeline/TrackThread.js +++ b/src/components/timeline/TrackThread.js @@ -277,6 +277,7 @@ class TimelineTrackThreadImpl extends PureComponent { enableCPUUsage={enableCPUUsage} maxThreadCPUDeltaPerMs={maxThreadCPUDeltaPerMs} implementationFilter={implementationFilter} + timelineType={timelineType} /> {trackType === 'expanded' ? ( 0) { + sampleStart--; + } + if (sampleEnd < table.length) { + sampleEnd++; + } + + return [sampleStart, sampleEnd]; +} + export function filterThreadSamplesToRange( thread: Thread, rangeStart: number, @@ -1393,58 +1422,39 @@ export function filterThreadSamplesToRange( return newThread; } -export function filterCounterToRange( - counter: Counter, - rangeStart: number, - rangeEnd: number -): Counter { - const filteredGroups = counter.sampleGroups.map((sampleGroup) => { - const samples = sampleGroup.samples; - let [sBegin, sEnd] = getSampleIndexRangeForSelection( - samples, - rangeStart, - rangeEnd - ); - - // Include the samples just before and after the selection range, so that charts will - // not be cut off at the edges. - if (sBegin > 0) { - sBegin--; - } - if (sEnd < samples.length) { - sEnd++; - } - - const count = samples.count.slice(sBegin, sEnd); - const number = samples.number.slice(sBegin, sEnd); - - if (sBegin === 0) { - // These lines zero out the first values of the counters, as they are unreliable. In - // addition, there are probably some missed counts in the memory counters, so the - // first memory number slowly creeps up over time, and becomes very unrealistic. - // In order to not be affected by these platform limitations, zero out the first - // counter values. - // - // "Memory counter in Gecko Profiler isn't cleared when starting a new capture" - // https://bugzilla.mozilla.org/show_bug.cgi?id=1520587 - count[0] = 0; - number[0] = 0; - } +/** + * Process the samples in the counter sample groups. + */ +export function processCounter(counter: Counter): Counter { + const processedGroups = counter.sampleGroups.map((sampleGroup) => { + const { samples } = sampleGroup; + const count = samples.count.slice(); + const number = samples.number.slice(); + + // These lines zero out the first values of the counters, as they are unreliable. In + // addition, there are probably some missed counts in the memory counters, so the + // first memory number slowly creeps up over time, and becomes very unrealistic. + // In order to not be affected by these platform limitations, zero out the first + // counter values. + // + // "Memory counter in Gecko Profiler isn't cleared when starting a new capture" + // https://bugzilla.mozilla.org/show_bug.cgi?id=1520587 + count[0] = 0; + number[0] = 0; return { ...sampleGroup, samples: { - time: samples.time.slice(sBegin, sEnd), + ...samples, number, count, - length: sEnd - sBegin, }, }; }); return { ...counter, - sampleGroups: filteredGroups, + sampleGroups: processedGroups, }; } @@ -1455,14 +1465,24 @@ export function filterCounterToRange( * accumulatedCounts array. */ export function accumulateCounterSamples( - samplesArray: Array + samplesArray: Array, + sampleRanges?: Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]> ): Array { - const accumulatedSamples = samplesArray.map((samples) => { + const accumulatedSamples = samplesArray.map((samples, index) => { let minCount = 0; let maxCount = 0; let accumulated = 0; - const accumulatedCounts = []; - for (let i = 0; i < samples.length; i++) { + const accumulatedCounts = Array(samples.length).fill(0); + // If a range is provided, use it instead. This will also include the + // samples right before and after the range. + const startSampleIndex = + sampleRanges && sampleRanges[index] ? sampleRanges[index][0] : 0; + const endSampleIndex = + sampleRanges && sampleRanges[index] + ? sampleRanges[index][1] + : samples.length; + + for (let i = startSampleIndex; i < endSampleIndex; i++) { accumulated += samples.count[i]; minCount = Math.min(accumulated, minCount); maxCount = Math.max(accumulated, maxCount); @@ -1484,14 +1504,26 @@ export function accumulateCounterSamples( /** * Compute the max counter sample counts per milliseconds to determine the range * of a counter. + * If a start-end range is provided, it only computes the max value between that + * range. */ export function computeMaxCounterSampleCountsPerMs( samplesArray: Array, - profileInterval: Milliseconds + profileInterval: Milliseconds, + sampleRanges?: Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]> ): Array { - const maxSampleCounts = samplesArray.map((samples) => { + const maxSampleCounts = samplesArray.map((samples, index) => { let maxCount = 0; - for (let i = 0; i < samples.length; i++) { + // If a range is provided, use it instead. This will also include the + // samples right before and after the range. + const startSampleIndex = + sampleRanges && sampleRanges[index] ? sampleRanges[index][0] : 0; + const endSampleIndex = + sampleRanges && sampleRanges[index] + ? sampleRanges[index][1] + : samples.length; + + for (let i = startSampleIndex; i < endSampleIndex; i++) { const count = samples.count[i]; const sampleTimeDeltaInMs = i === 0 ? profileInterval : samples.time[i] - samples.time[i - 1]; @@ -3403,3 +3435,32 @@ export function findAddressProofForFile( } return null; } + +/** + * Determines the timeline type by looking at the profile data. + * + * There are three options: + * 'cpu-category': If a profile has both category and cpu usage information. + * 'category': If a profile has category information but not the cpu usage. + * 'stack': If a profile doesn't have category or cpu usage information. + */ +export function determineTimelineType(profile: Profile): TimelineType { + if (!profile.meta.categories) { + // Profile doesn't have categories. We don't have enough information to draw + // a proper category view with activity graph. Use the stack chart instead. + // It can be either an imported or a very old profile. + return 'stack'; + } + + if ( + !profile.meta.sampleUnits || + !profile.threads.some((thread) => thread.samples.threadCPUDelta) + ) { + // Have category information but doesn't have the CPU usage information. + // Use 'category'. + return 'category'; + } + + // Have both category and CPU usage information. Use 'cpu-category'. + return 'cpu-category'; +} diff --git a/src/reducers/url-state.js b/src/reducers/url-state.js index 10b0a5f91c..57de6ac7e7 100644 --- a/src/reducers/url-state.js +++ b/src/reducers/url-state.js @@ -226,19 +226,16 @@ const transforms: Reducer = (state = {}, action) => { } }; -const timelineType: Reducer = (state = 'category', action) => { +const timelineType: Reducer = ( + state = 'cpu-category', + action +) => { switch (action.type) { - case 'PROFILE_LOADED': - if (!action.profile.meta.categories) { - // An imported profile did not provide its own categories. Use the stack view instead. - return 'stack'; - } - return state; case 'CHANGE_TIMELINE_TYPE': return action.timelineType; case 'VIEW_FULL_PROFILE': case 'VIEW_ACTIVE_TAB_PROFILE': - // The timelineType can be set at loadtime from a URL value. + // The timelineType can be set at load time from a URL value. // If it's not set from a URL value we provide a default value from this action. // When it's null we don't want to override the value that was set already. if (action.timelineType !== null) { diff --git a/src/selectors/app.js b/src/selectors/app.js index 3fbdc6bcfc..823d244b99 100644 --- a/src/selectors/app.js +++ b/src/selectors/app.js @@ -28,7 +28,6 @@ import { TRACK_IPC_HEIGHT, TRACK_PROCESS_BLANK_HEIGHT, TIMELINE_RULER_HEIGHT, - TIMELINE_SETTINGS_HEIGHT, TRACK_VISUAL_PROGRESS_HEIGHT, ACTIVE_TAB_TIMELINE_RESOURCES_HEADER_HEIGHT, TRACK_EVENT_DELAY_HEIGHT, @@ -214,9 +213,6 @@ export const getTimelineHeight: Selector = createSelector( return height; } case 'full': { - // Only the full view has the timeline settings panel. - height += TIMELINE_SETTINGS_HEIGHT; - for (const [trackIndex, globalTrack] of globalTracks.entries()) { if (!hiddenGlobalTracks.has(trackIndex)) { switch (globalTrack.type) { diff --git a/src/selectors/per-thread/composed.js b/src/selectors/per-thread/composed.js index db3f565890..28ddc2b160 100644 --- a/src/selectors/per-thread/composed.js +++ b/src/selectors/per-thread/composed.js @@ -5,7 +5,11 @@ // @flow import { createSelector } from 'reselect'; -import { tabSlugs, type TabSlug } from '../../app-logic/tabs-handling'; +import { + tabSlugs, + type TabSlug, + tabsShowingSampleData, +} from '../../app-logic/tabs-handling'; import type { Selector, @@ -22,6 +26,8 @@ import type { StackTimingByDepth, } from '../../profile-logic/stack-timing'; +import { ensureExists } from '../../utils/flow'; + /** * Infer the return type from the getStackAndSampleSelectorsPerThread function. This * is done that so that the local type definition with `Selector` is the canonical @@ -59,7 +65,15 @@ export function getComposedSelectorsPerThread( threadSelectors.getThread, threadSelectors.getIsNetworkChartEmptyInFullRange, threadSelectors.getJsTracerTable, - ({ processType }, isNetworkChartEmpty, jsTracerTable) => { + (thread, isNetworkChartEmpty, jsTracerTable) => { + const { + processType, + samples, + stackTable, + stringTable, + frameTable, + funcTable, + } = thread; if (processType === 'comparison') { // For a diffing tracks, we display only the calltree tab for now, because // other views make no or not much sense. @@ -76,6 +90,27 @@ export function getComposedSelectorsPerThread( if (!jsTracerTable) { visibleTabs = visibleTabs.filter((tabSlug) => tabSlug !== 'js-tracer'); } + let hasSamples = samples.length > 0 && stackTable.length > 0; + if (hasSamples) { + const stackIndex = ensureExists(samples.stack[0]); + if (stackTable.prefix[stackIndex] === null) { + // There's only a single stack frame, check if it's '(root)'. + const frameIndex = stackTable.frame[stackIndex]; + const funcIndex = frameTable.func[frameIndex]; + const stringIndex = funcTable.name[funcIndex]; + if (stringTable.getString(stringIndex) === '(root)') { + // If the first sample's stack is only the root, check if any other + // sample is different. + hasSamples = samples.stack.some((s) => s !== stackIndex); + } + } + } + + if (!hasSamples) { + visibleTabs = visibleTabs.filter( + (tabSlug) => !tabsShowingSampleData.includes(tabSlug) + ); + } return visibleTabs; } ); diff --git a/src/selectors/profile.js b/src/selectors/profile.js index b79335c058..64beb8093e 100644 --- a/src/selectors/profile.js +++ b/src/selectors/profile.js @@ -8,11 +8,12 @@ import * as Tracks from '../profile-logic/tracks'; import * as UrlState from './url-state'; import { ensureExists, assertExhaustiveCheck } from '../utils/flow'; import { - filterCounterToRange, accumulateCounterSamples, extractProfileFilterPageData, computeMaxCounterSampleCountsPerMs, getFriendlyThreadName, + processCounter, + getInclusiveSampleIndexRangeForSelection, } from '../profile-logic/profile-data'; import { IPCMarkerCorrelations, @@ -71,6 +72,7 @@ import type { MarkerSchema, MarkerSchemaByName, SampleUnits, + IndexIntoSamplesTable, } from 'firefox-profiler/types'; export const getProfileView: Selector = (state) => @@ -264,28 +266,40 @@ export const getCounterSelectors = (index: CounterIndex): CounterSelectors => { */ function _createCounterSelectors(counterIndex: CounterIndex) { const getCounter: Selector = (state) => - ensureExists( - getProfile(state).counters, - 'Attempting to get a counter by index, but no counters exist.' - )[counterIndex]; + processCounter( + ensureExists( + getProfile(state).counters, + 'Attempting to get a counter by index, but no counters exist.' + )[counterIndex] + ); const getDescription: Selector = (state) => getCounter(state).description; const getPid: Selector = (state) => getCounter(state).pid; - const getCommittedRangeFilteredCounter: Selector = createSelector( - getCounter, - getCommittedRange, - (counters, range) => filterCounterToRange(counters, range.start, range.end) + const getCommittedRangeCounterSampleRanges: Selector< + Array<[IndexIntoSamplesTable, IndexIntoSamplesTable]> + > = createSelector(getCounter, getCommittedRange, (counter, range) => + counter.sampleGroups.map((group) => + getInclusiveSampleIndexRangeForSelection( + group.samples, + range.start, + range.end + ) + ) ); const getAccumulateCounterSamples: Selector< Array - > = createSelector(getCommittedRangeFilteredCounter, (counters) => - accumulateCounterSamples( - counters.sampleGroups.map((group) => group.samples) - ) + > = createSelector( + getCounter, + getCommittedRangeCounterSampleRanges, + (counters, sampleRanges) => + accumulateCounterSamples( + counters.sampleGroups.map((group) => group.samples), + sampleRanges + ) ); const getMaxCounterSampleCountsPerMs: Selector> = @@ -303,15 +317,12 @@ function _createCounterSelectors(counterIndex: CounterIndex) { createSelector( getCounter, getProfileInterval, - getCommittedRange, - (counters, profileInterval, range) => + getCommittedRangeCounterSampleRanges, + (counters, profileInterval, sampleRange) => computeMaxCounterSampleCountsPerMs( - filterCounterToRange( - counters, - range.start, - range.end - ).sampleGroups.map((group) => group.samples), - profileInterval + counters.sampleGroups.map((group) => group.samples), + profileInterval, + sampleRange ) ); @@ -319,10 +330,10 @@ function _createCounterSelectors(counterIndex: CounterIndex) { getCounter, getDescription, getPid, - getCommittedRangeFilteredCounter, getAccumulateCounterSamples, getMaxCounterSampleCountsPerMs, getMaxRangeCounterSampleCountsPerMs, + getCommittedRangeCounterSampleRanges, }; } diff --git a/src/test/components/ProfileViewer.test.js b/src/test/components/ProfileViewer.test.js index c620621328..930e662da2 100644 --- a/src/test/components/ProfileViewer.test.js +++ b/src/test/components/ProfileViewer.test.js @@ -70,6 +70,6 @@ describe('ProfileViewer', function () { const { getState } = setup(); // Note: You should update this total height if you changed the height calculation algorithm. - expect(getTimelineHeight(getState())).toBe(1250); + expect(getTimelineHeight(getState())).toBe(1224); }); }); diff --git a/src/test/components/Timeline.test.js b/src/test/components/Timeline.test.js index 4724bf0e51..516f1043b1 100644 --- a/src/test/components/Timeline.test.js +++ b/src/test/components/Timeline.test.js @@ -10,10 +10,7 @@ import { render, screen } from 'firefox-profiler/test/fixtures/testing-library'; import { Timeline } from '../../components/timeline'; import { storeWithProfile } from '../fixtures/stores'; import { getProfileFromTextSamples } from '../fixtures/profiles/processed-profile'; -import { - autoMockCanvasContext, - flushDrawLog, -} from '../fixtures/mocks/canvas-context'; +import { autoMockCanvasContext } from '../fixtures/mocks/canvas-context'; import { autoMockDomRect } from 'firefox-profiler/test/fixtures/mocks/domrect.js'; import { mockRaf } from '../fixtures/mocks/request-animation-frame'; import { @@ -27,7 +24,6 @@ import { } from '../fixtures/utils'; import ReactDOM from 'react-dom'; import { - getTimelineTrackOrganization, selectedThreadSelectors, getRightClickedTrack, } from 'firefox-profiler/selectors'; @@ -374,28 +370,6 @@ describe('Timeline', function () { ); }); - it('renders the header', () => { - const flushRafCalls = mockRaf(); - window.devicePixelRatio = 1; - - const profile = _getProfileWithDroppedSamples(); - - const { container } = render( - - - - ); - - flushRafCalls(); - - const drawCalls = flushDrawLog(); - - expect(container.firstChild).toMatchSnapshot(); - expect(drawCalls).toMatchSnapshot(); - - delete window.devicePixelRatio; - }); - it('displays a context menu when right clicking global and local tracks', () => { const profile = getProfileWithNiceTracks(); @@ -444,57 +418,6 @@ describe('Timeline', function () { ); }); - // These tests are disabled for now because active tab view checkbox is disabled for now. - // TODO: Enable it again once we have that checbox back. - // eslint-disable-next-line jest/no-disabled-tests - describe.skip('TimelineSettingsActiveTabView', function () { - autoMockCanvasContext(); - - it('"Show active tab only" checkbox should not present in a profile without active tab metadata', () => { - const store = storeWithProfile(); - render( - - - - ); - - expect( - screen.queryByText('Show active tab only') - ).not.toBeInTheDocument(); - }); - - it('can switch between active tab view and advanced view', () => { - const profile = _getProfileWithDroppedSamples(); - profile.meta.configuration = { - threads: [], - features: [], - capacity: 1000000, - activeTabID: 123, - }; - const store = storeWithProfile(profile); - render( - - - - ); - - expect(getTimelineTrackOrganization(store.getState())).toEqual({ - type: 'full', - }); - - fireFullClick(screen.getByText('Show active tab only')); - expect(getTimelineTrackOrganization(store.getState())).toEqual({ - type: 'active-tab', - tabID: 123, - }); - - fireFullClick(screen.getByText('Show active tab only')); - expect(getTimelineTrackOrganization(store.getState())).toEqual({ - type: 'full', - }); - }); - }); - describe('TimelineSettingsHiddenTracks', () => { it('resets "rightClickedTrack" state when clicked', () => { const profile = _getProfileWithDroppedSamples(); diff --git a/src/test/components/__snapshots__/SampleTooltipContents.test.js.snap b/src/test/components/__snapshots__/SampleTooltipContents.test.js.snap index 80a24e7f0a..825501423e 100644 --- a/src/test/components/__snapshots__/SampleTooltipContents.test.js.snap +++ b/src/test/components/__snapshots__/SampleTooltipContents.test.js.snap @@ -298,10 +298,10 @@ exports[`SampleTooltipContents renders the sample with "variable CPU cycles" CPU
50% (average over 1.0ms)
-
+
@@ -410,10 +410,10 @@ exports[`SampleTooltipContents renders the sample with ns CPU usage information
70% (average over 1.0ms)
-
+
@@ -522,10 +522,10 @@ exports[`SampleTooltipContents renders the sample with µs CPU usage information
45% (average over 1.0ms)
-
+
diff --git a/src/test/components/__snapshots__/Timeline.test.js.snap b/src/test/components/__snapshots__/Timeline.test.js.snap deleted file mode 100644 index 9bb55f65cf..0000000000 --- a/src/test/components/__snapshots__/Timeline.test.js.snap +++ /dev/null @@ -1,8133 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Timeline renders the header 1`] = ` -
-
-
- Graph type: - - -
-
-
-`; - -exports[`Timeline renders the header 2`] = ` -Array [ - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.25, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.75, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#d7d7db60", - ], - Array [ - 0.25, - "#d7d7db60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#d7d7db60", - ], - Array [ - 0.75, - "#d7d7db60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "transparent", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "transparent", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#6200a460", - ], - Array [ - "addColorStop", - 0.25, - "#6200a460", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#6200a460", - ], - Array [ - "addColorStop", - 0.75, - "#6200a460", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#6200a460", - ], - Array [ - 0.25, - "#6200a460", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#6200a460", - ], - Array [ - 0.75, - "#6200a460", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.25, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.75, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#ffe90060", - ], - Array [ - 0.25, - "#ffe90060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#ffe90060", - ], - Array [ - 0.75, - "#ffe90060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#ff940060", - ], - Array [ - "addColorStop", - 0.25, - "#ff940060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#ff940060", - ], - Array [ - "addColorStop", - 0.75, - "#ff940060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#ff940060", - ], - Array [ - 0.25, - "#ff940060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#ff940060", - ], - Array [ - 0.75, - "#ff940060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.25, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.75, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#45a1ff60", - ], - Array [ - 0.25, - "#45a1ff60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#45a1ff60", - ], - Array [ - 0.75, - "#45a1ff60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.25, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.75, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#12bc0060", - ], - Array [ - 0.25, - "#12bc0060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#12bc0060", - ], - Array [ - 0.75, - "#12bc0060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#0060df60", - ], - Array [ - "addColorStop", - 0.25, - "#0060df60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#0060df60", - ], - Array [ - "addColorStop", - 0.75, - "#0060df60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#0060df60", - ], - Array [ - 0.25, - "#0060df60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#0060df60", - ], - Array [ - 0.75, - "#0060df60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "set globalCompositeOperation", - "lighter", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - "#d7d7db", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - "#ffe900", - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - "#6200a4", - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - "#12bc00", - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - "#0060df", - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - "#ff9400", - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - "#45a1ff", - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - -0.4, - 0, - 0.8, - 0, - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.25, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.75, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#d7d7db60", - ], - Array [ - 0.25, - "#d7d7db60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#d7d7db60", - ], - Array [ - 0.75, - "#d7d7db60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "transparent", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "transparent", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#6200a460", - ], - Array [ - "addColorStop", - 0.25, - "#6200a460", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#6200a460", - ], - Array [ - "addColorStop", - 0.75, - "#6200a460", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#6200a460", - ], - Array [ - 0.25, - "#6200a460", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#6200a460", - ], - Array [ - 0.75, - "#6200a460", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.25, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.75, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#ffe90060", - ], - Array [ - 0.25, - "#ffe90060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#ffe90060", - ], - Array [ - 0.75, - "#ffe90060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#ff940060", - ], - Array [ - "addColorStop", - 0.25, - "#ff940060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#ff940060", - ], - Array [ - "addColorStop", - 0.75, - "#ff940060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#ff940060", - ], - Array [ - 0.25, - "#ff940060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#ff940060", - ], - Array [ - 0.75, - "#ff940060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.25, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.75, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#45a1ff60", - ], - Array [ - 0.25, - "#45a1ff60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#45a1ff60", - ], - Array [ - 0.75, - "#45a1ff60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.25, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.75, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#12bc0060", - ], - Array [ - 0.25, - "#12bc0060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#12bc0060", - ], - Array [ - 0.75, - "#12bc0060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#0060df60", - ], - Array [ - "addColorStop", - 0.25, - "#0060df60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#0060df60", - ], - Array [ - "addColorStop", - 0.75, - "#0060df60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#0060df60", - ], - Array [ - 0.25, - "#0060df60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#0060df60", - ], - Array [ - 0.75, - "#0060df60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "set globalCompositeOperation", - "lighter", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - "#d7d7db", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - "#ffe900", - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - "#6200a4", - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - "#12bc00", - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - "#0060df", - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - "#ff9400", - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - "#45a1ff", - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - -0.4, - 0, - 0.8, - 0, - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.25, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.75, - "#d7d7db60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#d7d7db60", - ], - Array [ - 0.25, - "#d7d7db60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#d7d7db60", - ], - Array [ - 0.75, - "#d7d7db60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "transparent", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "transparent", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#6200a460", - ], - Array [ - "addColorStop", - 0.25, - "#6200a460", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#6200a460", - ], - Array [ - "addColorStop", - 0.75, - "#6200a460", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#6200a460", - ], - Array [ - 0.25, - "#6200a460", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#6200a460", - ], - Array [ - 0.75, - "#6200a460", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.25, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.75, - "#ffe90060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#ffe90060", - ], - Array [ - 0.25, - "#ffe90060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#ffe90060", - ], - Array [ - 0.75, - "#ffe90060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#ff940060", - ], - Array [ - "addColorStop", - 0.25, - "#ff940060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#ff940060", - ], - Array [ - "addColorStop", - 0.75, - "#ff940060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#ff940060", - ], - Array [ - 0.25, - "#ff940060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#ff940060", - ], - Array [ - 0.75, - "#ff940060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.25, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.75, - "#45a1ff60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#45a1ff60", - ], - Array [ - 0.25, - "#45a1ff60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#45a1ff60", - ], - Array [ - 0.75, - "#45a1ff60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.25, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.75, - "#12bc0060", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#12bc0060", - ], - Array [ - 0.25, - "#12bc0060", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#12bc0060", - ], - Array [ - 0.75, - "#12bc0060", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "createLinearGradient", - 0, - 0, - 4, - 4, - ], - Array [ - "addColorStop", - 0, - "#0060df60", - ], - Array [ - "addColorStop", - 0.25, - "#0060df60", - ], - Array [ - "addColorStop", - 0.25, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "transparent", - ], - Array [ - "addColorStop", - 0.5, - "#0060df60", - ], - Array [ - "addColorStop", - 0.75, - "#0060df60", - ], - Array [ - "addColorStop", - 0.75, - "transparent", - ], - Array [ - "addColorStop", - 1, - "transparent", - ], - Array [ - "set fillStyle", - Object { - "addColorStop": [MockFunction] { - "calls": Array [ - Array [ - 0, - "#0060df60", - ], - Array [ - 0.25, - "#0060df60", - ], - Array [ - 0.25, - "transparent", - ], - Array [ - 0.5, - "transparent", - ], - Array [ - 0.5, - "#0060df60", - ], - Array [ - 0.75, - "#0060df60", - ], - Array [ - 0.75, - "transparent", - ], - Array [ - 1, - "transparent", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - Object { - "type": "return", - "value": undefined, - }, - ], - }, - }, - ], - Array [ - "fillRect", - 0, - 0, - 4, - 4, - ], - Array [ - "createPattern", - , - "repeat", - ], - Array [ - "set globalCompositeOperation", - "lighter", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - "#d7d7db", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - "#ffe900", - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - "#6200a4", - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - "#12bc00", - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - "#0060df", - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - "#ff9400", - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - "#45a1ff", - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - -0.4, - 0, - 0.8, - 0, - ], - Array [ - "set globalCompositeOperation", - "lighter", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - "#d7d7db", - ], - Array [ - "beginPath", - ], - Array [ - "moveTo", - 0, - 300, - ], - Array [ - "lineTo", - 1, - 300, - ], - Array [ - "lineTo", - 2, - 300, - ], - Array [ - "lineTo", - 3, - 300, - ], - Array [ - "lineTo", - 4, - 300, - ], - Array [ - "lineTo", - 5, - 300, - ], - Array [ - "lineTo", - 6, - 300, - ], - Array [ - "lineTo", - 7, - 300, - ], - Array [ - "lineTo", - 8, - 300, - ], - Array [ - "lineTo", - 9, - 300, - ], - Array [ - "lineTo", - 10, - 300, - ], - Array [ - "lineTo", - 11, - 300, - ], - Array [ - "lineTo", - 12, - 300, - ], - Array [ - "lineTo", - 13, - 300, - ], - Array [ - "lineTo", - 14, - 300, - ], - Array [ - "lineTo", - 15, - 300, - ], - Array [ - "lineTo", - 16, - 300, - ], - Array [ - "lineTo", - 17, - 300, - ], - Array [ - "lineTo", - 18, - 300, - ], - Array [ - "lineTo", - 19, - 300, - ], - Array [ - "lineTo", - 20, - 300, - ], - Array [ - "lineTo", - 21, - 300, - ], - Array [ - "lineTo", - 22, - 300, - ], - Array [ - "lineTo", - 23, - 300, - ], - Array [ - "lineTo", - 24, - 300, - ], - Array [ - "lineTo", - 25, - 300, - ], - Array [ - "lineTo", - 26, - 300, - ], - Array [ - "lineTo", - 27, - 300, - ], - Array [ - "lineTo", - 28, - 300, - ], - Array [ - "lineTo", - 29, - 300, - ], - Array [ - "lineTo", - 30, - 300, - ], - Array [ - "lineTo", - 31, - 300, - ], - Array [ - "lineTo", - 32, - 300, - ], - Array [ - "lineTo", - 33, - 300, - ], - Array [ - "lineTo", - 34, - 300, - ], - Array [ - "lineTo", - 35, - 300, - ], - Array [ - "lineTo", - 36, - 300, - ], - Array [ - "lineTo", - 37, - 300, - ], - Array [ - "lineTo", - 38, - 300, - ], - Array [ - "lineTo", - 39, - 300, - ], - Array [ - "lineTo", - 40, - 300, - ], - Array [ - "lineTo", - 41, - 300, - ], - Array [ - "lineTo", - 42, - 300, - ], - Array [ - "lineTo", - 43, - 300, - ], - Array [ - "lineTo", - 44, - 300, - ], - Array [ - "lineTo", - 45, - 300, - ], - Array [ - "lineTo", - 46, - 300, - ], - Array [ - "lineTo", - 47, - 300, - ], - Array [ - "lineTo", - 48, - 300, - ], - Array [ - "lineTo", - 49, - 300, - ], - Array [ - "lineTo", - 50, - 300, - ], - Array [ - "lineTo", - 51, - 300, - ], - Array [ - "lineTo", - 52, - 300, - ], - Array [ - "lineTo", - 53, - 300, - ], - Array [ - "lineTo", - 54, - 300, - ], - Array [ - "lineTo", - 55, - 300, - ], - Array [ - "lineTo", - 56, - 300, - ], - Array [ - "lineTo", - 57, - 300, - ], - Array [ - "lineTo", - 58, - 300, - ], - Array [ - "lineTo", - 59, - 300, - ], - Array [ - "lineTo", - 60, - 300, - ], - Array [ - "lineTo", - 61, - 300, - ], - Array [ - "lineTo", - 62, - 300, - ], - Array [ - "lineTo", - 63, - 300, - ], - Array [ - "lineTo", - 64, - 300, - ], - Array [ - "lineTo", - 65, - 300, - ], - Array [ - "lineTo", - 66, - 300, - ], - Array [ - "lineTo", - 67, - 300, - ], - Array [ - "lineTo", - 68, - 300, - ], - Array [ - "lineTo", - 69, - 300, - ], - Array [ - "lineTo", - 70, - 300, - ], - Array [ - "lineTo", - 71, - 300, - ], - Array [ - "lineTo", - 72, - 300, - ], - Array [ - "lineTo", - 73, - 300, - ], - Array [ - "lineTo", - 74, - 300, - ], - Array [ - "lineTo", - 75, - 300, - ], - Array [ - "lineTo", - 76, - 300, - ], - Array [ - "lineTo", - 77, - 300, - ], - Array [ - "lineTo", - 78, - 300, - ], - Array [ - "lineTo", - 79, - 300, - ], - Array [ - "lineTo", - 80, - 300, - ], - Array [ - "lineTo", - 81, - 300, - ], - Array [ - "lineTo", - 82, - 300, - ], - Array [ - "lineTo", - 83, - 300, - ], - Array [ - "lineTo", - 84, - 300, - ], - Array [ - "lineTo", - 85, - 300, - ], - Array [ - "lineTo", - 86, - 300, - ], - Array [ - "lineTo", - 87, - 300, - ], - Array [ - "lineTo", - 88, - 300, - ], - Array [ - "lineTo", - 89, - 300, - ], - Array [ - "lineTo", - 90, - 300, - ], - Array [ - "lineTo", - 91, - 300, - ], - Array [ - "lineTo", - 92, - 300, - ], - Array [ - "lineTo", - 93, - 300, - ], - Array [ - "lineTo", - 94, - 300, - ], - Array [ - "lineTo", - 95, - 300, - ], - Array [ - "lineTo", - 96, - 300, - ], - Array [ - "lineTo", - 97, - 300, - ], - Array [ - "lineTo", - 98, - 300, - ], - Array [ - "lineTo", - 99, - 300, - ], - Array [ - "lineTo", - 100, - 300, - ], - Array [ - "lineTo", - 101, - 300, - ], - Array [ - "lineTo", - 102, - 300, - ], - Array [ - "lineTo", - 103, - 300, - ], - Array [ - "lineTo", - 104, - 300, - ], - Array [ - "lineTo", - 105, - 300, - ], - Array [ - "lineTo", - 106, - 300, - ], - Array [ - "lineTo", - 107, - 300, - ], - Array [ - "lineTo", - 108, - 300, - ], - Array [ - "lineTo", - 109, - 300, - ], - Array [ - "lineTo", - 110, - 300, - ], - Array [ - "lineTo", - 111, - 300, - ], - Array [ - "lineTo", - 112, - 300, - ], - Array [ - "lineTo", - 113, - 300, - ], - Array [ - "lineTo", - 114, - 300, - ], - Array [ - "lineTo", - 115, - 300, - ], - Array [ - "lineTo", - 116, - 300, - ], - Array [ - "lineTo", - 117, - 300, - ], - Array [ - "lineTo", - 118, - 300, - ], - Array [ - "lineTo", - 119, - 300, - ], - Array [ - "lineTo", - 120, - 300, - ], - Array [ - "lineTo", - 121, - 300, - ], - Array [ - "lineTo", - 122, - 300, - ], - Array [ - "lineTo", - 123, - 300, - ], - Array [ - "lineTo", - 124, - 300, - ], - Array [ - "lineTo", - 125, - 300, - ], - Array [ - "lineTo", - 126, - 300, - ], - Array [ - "lineTo", - 127, - 300, - ], - Array [ - "lineTo", - 128, - 300, - ], - Array [ - "lineTo", - 129, - 300, - ], - Array [ - "lineTo", - 130, - 300, - ], - Array [ - "lineTo", - 131, - 300, - ], - Array [ - "lineTo", - 132, - 300, - ], - Array [ - "lineTo", - 133, - 300, - ], - Array [ - "lineTo", - 134, - 300, - ], - Array [ - "lineTo", - 135, - 300, - ], - Array [ - "lineTo", - 136, - 300, - ], - Array [ - "lineTo", - 137, - 300, - ], - Array [ - "lineTo", - 138, - 300, - ], - Array [ - "lineTo", - 139, - 300, - ], - Array [ - "lineTo", - 140, - 300, - ], - Array [ - "lineTo", - 141, - 300, - ], - Array [ - "lineTo", - 142, - 300, - ], - Array [ - "lineTo", - 143, - 300, - ], - Array [ - "lineTo", - 144, - 300, - ], - Array [ - "lineTo", - 145, - 300, - ], - Array [ - "lineTo", - 146, - 300, - ], - Array [ - "lineTo", - 147, - 300, - ], - Array [ - "lineTo", - 148, - 300, - ], - Array [ - "lineTo", - 149, - 300, - ], - Array [ - "lineTo", - 150, - 300, - ], - Array [ - "lineTo", - 151, - 300, - ], - Array [ - "lineTo", - 152, - 300, - ], - Array [ - "lineTo", - 153, - 300, - ], - Array [ - "lineTo", - 154, - 300, - ], - Array [ - "lineTo", - 155, - 300, - ], - Array [ - "lineTo", - 156, - 300, - ], - Array [ - "lineTo", - 157, - 300, - ], - Array [ - "lineTo", - 158, - 300, - ], - Array [ - "lineTo", - 159, - 300, - ], - Array [ - "lineTo", - 160, - 300, - ], - Array [ - "lineTo", - 161, - 300, - ], - Array [ - "lineTo", - 162, - 300, - ], - Array [ - "lineTo", - 163, - 300, - ], - Array [ - "lineTo", - 164, - 300, - ], - Array [ - "lineTo", - 165, - 300, - ], - Array [ - "lineTo", - 166, - 300, - ], - Array [ - "lineTo", - 167, - 300, - ], - Array [ - "lineTo", - 168, - 300, - ], - Array [ - "lineTo", - 169, - 300, - ], - Array [ - "lineTo", - 170, - 300, - ], - Array [ - "lineTo", - 171, - 300, - ], - Array [ - "lineTo", - 172, - 300, - ], - Array [ - "lineTo", - 173, - 300, - ], - Array [ - "lineTo", - 174, - 300, - ], - Array [ - "lineTo", - 175, - 300, - ], - Array [ - "lineTo", - 176, - 300, - ], - Array [ - "lineTo", - 177, - 300, - ], - Array [ - "lineTo", - 178, - 300, - ], - Array [ - "lineTo", - 179, - 300, - ], - Array [ - "lineTo", - 180, - 300, - ], - Array [ - "lineTo", - 181, - 300, - ], - Array [ - "lineTo", - 182, - 300, - ], - Array [ - "lineTo", - 183, - 300, - ], - Array [ - "lineTo", - 184, - 300, - ], - Array [ - "lineTo", - 185, - 300, - ], - Array [ - "lineTo", - 186, - 300, - ], - Array [ - "lineTo", - 187, - 300, - ], - Array [ - "lineTo", - 188, - 300, - ], - Array [ - "lineTo", - 189, - 300, - ], - Array [ - "lineTo", - 190, - 300, - ], - Array [ - "lineTo", - 191, - 300, - ], - Array [ - "lineTo", - 192, - 300, - ], - Array [ - "lineTo", - 193, - 300, - ], - Array [ - "lineTo", - 194, - 300, - ], - Array [ - "lineTo", - 195, - 300, - ], - Array [ - "lineTo", - 196, - 300, - ], - Array [ - "lineTo", - 195, - 298.47619039937854, - ], - Array [ - "lineTo", - 194, - 293.71428564190865, - ], - Array [ - "lineTo", - 193, - 283.9999996125698, - ], - Array [ - "lineTo", - 192, - 267.6190450787544, - ], - Array [ - "lineTo", - 191, - 242.8571417927742, - ], - Array [ - "lineTo", - 190, - 211.04761362075806, - ], - Array [ - "lineTo", - 189, - 173.90475869178772, - ], - Array [ - "lineTo", - 188, - 134.6666693687439, - ], - Array [ - "lineTo", - 187, - 96.76190614700317, - ], - Array [ - "lineTo", - 186, - 63.61904740333557, - ], - Array [ - "lineTo", - 185, - 37.14285492897034, - ], - Array [ - "lineTo", - 184, - 19.04761791229248, - ], - Array [ - "lineTo", - 183, - 7.999992370605469, - ], - Array [ - "lineTo", - 182, - 2.2857069969177246, - ], - Array [ - "lineTo", - 181, - 0.19047260284423828, - ], - Array [ - "lineTo", - 180, - 0, - ], - Array [ - "lineTo", - 179, - 0, - ], - Array [ - "lineTo", - 178, - 0, - ], - Array [ - "lineTo", - 177, - 0, - ], - Array [ - "lineTo", - 176, - 0, - ], - Array [ - "lineTo", - 175, - 0, - ], - Array [ - "lineTo", - 174, - 0, - ], - Array [ - "lineTo", - 173, - 0, - ], - Array [ - "lineTo", - 172, - 0, - ], - Array [ - "lineTo", - 171, - 0, - ], - Array [ - "lineTo", - 170, - 0, - ], - Array [ - "lineTo", - 169, - 0, - ], - Array [ - "lineTo", - 168, - 0, - ], - Array [ - "lineTo", - 167, - 0, - ], - Array [ - "lineTo", - 166, - 0, - ], - Array [ - "lineTo", - 165, - 0, - ], - Array [ - "lineTo", - 164, - 0, - ], - Array [ - "lineTo", - 163, - 0, - ], - Array [ - "lineTo", - 162, - 0, - ], - Array [ - "lineTo", - 161, - 0, - ], - Array [ - "lineTo", - 160, - 0, - ], - Array [ - "lineTo", - 159, - 0, - ], - Array [ - "lineTo", - 158, - 0, - ], - Array [ - "lineTo", - 157, - 0, - ], - Array [ - "lineTo", - 156, - 0, - ], - Array [ - "lineTo", - 155, - 0, - ], - Array [ - "lineTo", - 154, - 0, - ], - Array [ - "lineTo", - 153, - 0, - ], - Array [ - "lineTo", - 152, - 0, - ], - Array [ - "lineTo", - 151, - 0, - ], - Array [ - "lineTo", - 150, - 0, - ], - Array [ - "lineTo", - 149, - 0, - ], - Array [ - "lineTo", - 148, - 0, - ], - Array [ - "lineTo", - 147, - 0, - ], - Array [ - "lineTo", - 146, - 0, - ], - Array [ - "lineTo", - 145, - 0, - ], - Array [ - "lineTo", - 144, - 0, - ], - Array [ - "lineTo", - 143, - 0, - ], - Array [ - "lineTo", - 142, - 0, - ], - Array [ - "lineTo", - 141, - 0, - ], - Array [ - "lineTo", - 140, - 0, - ], - Array [ - "lineTo", - 139, - 0, - ], - Array [ - "lineTo", - 138, - 0, - ], - Array [ - "lineTo", - 137, - 0, - ], - Array [ - "lineTo", - 136, - 0, - ], - Array [ - "lineTo", - 135, - 0, - ], - Array [ - "lineTo", - 134, - 0, - ], - Array [ - "lineTo", - 133, - 0, - ], - Array [ - "lineTo", - 132, - 0, - ], - Array [ - "lineTo", - 131, - 0, - ], - Array [ - "lineTo", - 130, - 0, - ], - Array [ - "lineTo", - 129, - 0, - ], - Array [ - "lineTo", - 128, - 0, - ], - Array [ - "lineTo", - 127, - 0, - ], - Array [ - "lineTo", - 126, - 0, - ], - Array [ - "lineTo", - 125, - 0, - ], - Array [ - "lineTo", - 124, - 0, - ], - Array [ - "lineTo", - 123, - 0, - ], - Array [ - "lineTo", - 122, - 0, - ], - Array [ - "lineTo", - 121, - 0, - ], - Array [ - "lineTo", - 120, - 0, - ], - Array [ - "lineTo", - 119, - 0, - ], - Array [ - "lineTo", - 118, - 0, - ], - Array [ - "lineTo", - 117, - 0, - ], - Array [ - "lineTo", - 116, - 0, - ], - Array [ - "lineTo", - 115, - 0, - ], - Array [ - "lineTo", - 114, - 0, - ], - Array [ - "lineTo", - 113, - 0, - ], - Array [ - "lineTo", - 112, - 0, - ], - Array [ - "lineTo", - 111, - 0, - ], - Array [ - "lineTo", - 110, - 0, - ], - Array [ - "lineTo", - 109, - 0, - ], - Array [ - "lineTo", - 108, - 0, - ], - Array [ - "lineTo", - 107, - 0, - ], - Array [ - "lineTo", - 106, - 0, - ], - Array [ - "lineTo", - 105, - 0, - ], - Array [ - "lineTo", - 104, - 0, - ], - Array [ - "lineTo", - 103, - 0, - ], - Array [ - "lineTo", - 102, - 0, - ], - Array [ - "lineTo", - 101, - 0, - ], - Array [ - "lineTo", - 100, - 0, - ], - Array [ - "lineTo", - 99, - 0, - ], - Array [ - "lineTo", - 98, - 0, - ], - Array [ - "lineTo", - 97, - 0, - ], - Array [ - "lineTo", - 96, - 0, - ], - Array [ - "lineTo", - 95, - 0, - ], - Array [ - "lineTo", - 94, - 0, - ], - Array [ - "lineTo", - 93, - 0, - ], - Array [ - "lineTo", - 92, - 0, - ], - Array [ - "lineTo", - 91, - 0, - ], - Array [ - "lineTo", - 90, - 0, - ], - Array [ - "lineTo", - 89, - 0, - ], - Array [ - "lineTo", - 88, - 0, - ], - Array [ - "lineTo", - 87, - 0, - ], - Array [ - "lineTo", - 86, - 0, - ], - Array [ - "lineTo", - 85, - 0, - ], - Array [ - "lineTo", - 84, - 0, - ], - Array [ - "lineTo", - 83, - 0, - ], - Array [ - "lineTo", - 82, - 0, - ], - Array [ - "lineTo", - 81, - 0, - ], - Array [ - "lineTo", - 80, - 0, - ], - Array [ - "lineTo", - 79, - 0, - ], - Array [ - "lineTo", - 78, - 0, - ], - Array [ - "lineTo", - 77, - 0, - ], - Array [ - "lineTo", - 76, - 0, - ], - Array [ - "lineTo", - 75, - 0, - ], - Array [ - "lineTo", - 74, - 0, - ], - Array [ - "lineTo", - 73, - 0, - ], - Array [ - "lineTo", - 72, - 0, - ], - Array [ - "lineTo", - 71, - 0, - ], - Array [ - "lineTo", - 70, - 0, - ], - Array [ - "lineTo", - 69, - 0, - ], - Array [ - "lineTo", - 68, - 0, - ], - Array [ - "lineTo", - 67, - 0, - ], - Array [ - "lineTo", - 66, - 0, - ], - Array [ - "lineTo", - 65, - 0, - ], - Array [ - "lineTo", - 64, - 0, - ], - Array [ - "lineTo", - 63, - 0, - ], - Array [ - "lineTo", - 62, - 0, - ], - Array [ - "lineTo", - 61, - 0, - ], - Array [ - "lineTo", - 60, - 0, - ], - Array [ - "lineTo", - 59, - 0, - ], - Array [ - "lineTo", - 58, - 0, - ], - Array [ - "lineTo", - 57, - 0, - ], - Array [ - "lineTo", - 56, - 0, - ], - Array [ - "lineTo", - 55, - 0, - ], - Array [ - "lineTo", - 54, - 0, - ], - Array [ - "lineTo", - 53, - 0, - ], - Array [ - "lineTo", - 52, - 0, - ], - Array [ - "lineTo", - 51, - 0, - ], - Array [ - "lineTo", - 50, - 0, - ], - Array [ - "lineTo", - 49, - 0, - ], - Array [ - "lineTo", - 48, - 0, - ], - Array [ - "lineTo", - 47, - 0, - ], - Array [ - "lineTo", - 46, - 0, - ], - Array [ - "lineTo", - 45, - 0, - ], - Array [ - "lineTo", - 44, - 0, - ], - Array [ - "lineTo", - 43, - 0, - ], - Array [ - "lineTo", - 42, - 0, - ], - Array [ - "lineTo", - 41, - 0, - ], - Array [ - "lineTo", - 40, - 0, - ], - Array [ - "lineTo", - 39, - 0, - ], - Array [ - "lineTo", - 38, - 0, - ], - Array [ - "lineTo", - 37, - 0, - ], - Array [ - "lineTo", - 36, - 0, - ], - Array [ - "lineTo", - 35, - 0, - ], - Array [ - "lineTo", - 34, - 0, - ], - Array [ - "lineTo", - 33, - 0, - ], - Array [ - "lineTo", - 32, - 0, - ], - Array [ - "lineTo", - 31, - 0, - ], - Array [ - "lineTo", - 30, - 0, - ], - Array [ - "lineTo", - 29, - 0, - ], - Array [ - "lineTo", - 28, - 0, - ], - Array [ - "lineTo", - 27, - 0, - ], - Array [ - "lineTo", - 26, - 0, - ], - Array [ - "lineTo", - 25, - 0, - ], - Array [ - "lineTo", - 24, - 0, - ], - Array [ - "lineTo", - 23, - 0, - ], - Array [ - "lineTo", - 22, - 0, - ], - Array [ - "lineTo", - 21, - 0, - ], - Array [ - "lineTo", - 20, - 0, - ], - Array [ - "lineTo", - 19, - 0, - ], - Array [ - "lineTo", - 18, - 0, - ], - Array [ - "lineTo", - 17, - 0, - ], - Array [ - "lineTo", - 16, - 0, - ], - Array [ - "lineTo", - 15, - 0, - ], - Array [ - "lineTo", - 14, - 0, - ], - Array [ - "lineTo", - 13, - 0, - ], - Array [ - "lineTo", - 12, - 0, - ], - Array [ - "lineTo", - 11, - 0, - ], - Array [ - "lineTo", - 10, - 0, - ], - Array [ - "lineTo", - 9, - 0, - ], - Array [ - "lineTo", - 8, - 0, - ], - Array [ - "lineTo", - 7, - 0, - ], - Array [ - "lineTo", - 6, - 1.7142891883850098, - ], - Array [ - "lineTo", - 5, - 6.8571388721466064, - ], - Array [ - "lineTo", - 4, - 17.142856121063232, - ], - Array [ - "lineTo", - 3, - 39.42856192588806, - ], - Array [ - "lineTo", - 2, - 73.71429204940796, - ], - Array [ - "lineTo", - 1, - 131.99999928474426, - ], - Array [ - "lineTo", - 0, - 186.85713708400726, - ], - Array [ - "closePath", - ], - Array [ - "fill", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - "#ffe900", - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - "#6200a4", - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - "#12bc00", - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - "#0060df", - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - "#ff9400", - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - "#45a1ff", - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - -5, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 17.22222222222222, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 39.44444444444444, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 61.66666666666666, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 83.88888888888889, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 106.11111111111111, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 128.33333333333331, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 150.55555555555554, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 172.77777777777777, - 0, - 10, - 300, - ], - Array [ - "set globalCompositeOperation", - "lighter", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - "#d7d7db", - ], - Array [ - "beginPath", - ], - Array [ - "moveTo", - 70, - 300, - ], - Array [ - "lineTo", - 71, - 300, - ], - Array [ - "lineTo", - 72, - 300, - ], - Array [ - "lineTo", - 73, - 300, - ], - Array [ - "lineTo", - 74, - 300, - ], - Array [ - "lineTo", - 75, - 300, - ], - Array [ - "lineTo", - 76, - 300, - ], - Array [ - "lineTo", - 77, - 300, - ], - Array [ - "lineTo", - 78, - 300, - ], - Array [ - "lineTo", - 79, - 300, - ], - Array [ - "lineTo", - 80, - 300, - ], - Array [ - "lineTo", - 81, - 300, - ], - Array [ - "lineTo", - 82, - 300, - ], - Array [ - "lineTo", - 83, - 300, - ], - Array [ - "lineTo", - 84, - 300, - ], - Array [ - "lineTo", - 85, - 300, - ], - Array [ - "lineTo", - 86, - 300, - ], - Array [ - "lineTo", - 87, - 300, - ], - Array [ - "lineTo", - 88, - 300, - ], - Array [ - "lineTo", - 89, - 300, - ], - Array [ - "lineTo", - 90, - 300, - ], - Array [ - "lineTo", - 91, - 300, - ], - Array [ - "lineTo", - 92, - 300, - ], - Array [ - "lineTo", - 93, - 300, - ], - Array [ - "lineTo", - 94, - 300, - ], - Array [ - "lineTo", - 95, - 300, - ], - Array [ - "lineTo", - 96, - 300, - ], - Array [ - "lineTo", - 97, - 300, - ], - Array [ - "lineTo", - 98, - 300, - ], - Array [ - "lineTo", - 99, - 300, - ], - Array [ - "lineTo", - 100, - 300, - ], - Array [ - "lineTo", - 101, - 300, - ], - Array [ - "lineTo", - 102, - 300, - ], - Array [ - "lineTo", - 103, - 300, - ], - Array [ - "lineTo", - 104, - 300, - ], - Array [ - "lineTo", - 105, - 300, - ], - Array [ - "lineTo", - 106, - 300, - ], - Array [ - "lineTo", - 107, - 300, - ], - Array [ - "lineTo", - 108, - 300, - ], - Array [ - "lineTo", - 109, - 300, - ], - Array [ - "lineTo", - 110, - 300, - ], - Array [ - "lineTo", - 111, - 300, - ], - Array [ - "lineTo", - 112, - 300, - ], - Array [ - "lineTo", - 113, - 300, - ], - Array [ - "lineTo", - 114, - 300, - ], - Array [ - "lineTo", - 115, - 300, - ], - Array [ - "lineTo", - 116, - 300, - ], - Array [ - "lineTo", - 117, - 300, - ], - Array [ - "lineTo", - 118, - 300, - ], - Array [ - "lineTo", - 119, - 300, - ], - Array [ - "lineTo", - 120, - 300, - ], - Array [ - "lineTo", - 121, - 300, - ], - Array [ - "lineTo", - 122, - 300, - ], - Array [ - "lineTo", - 123, - 300, - ], - Array [ - "lineTo", - 124, - 300, - ], - Array [ - "lineTo", - 125, - 300, - ], - Array [ - "lineTo", - 126, - 300, - ], - Array [ - "lineTo", - 127, - 300, - ], - Array [ - "lineTo", - 128, - 300, - ], - Array [ - "lineTo", - 129, - 300, - ], - Array [ - "lineTo", - 130, - 300, - ], - Array [ - "lineTo", - 131, - 300, - ], - Array [ - "lineTo", - 132, - 300, - ], - Array [ - "lineTo", - 133, - 300, - ], - Array [ - "lineTo", - 134, - 300, - ], - Array [ - "lineTo", - 135, - 300, - ], - Array [ - "lineTo", - 136, - 300, - ], - Array [ - "lineTo", - 137, - 300, - ], - Array [ - "lineTo", - 138, - 300, - ], - Array [ - "lineTo", - 139, - 300, - ], - Array [ - "lineTo", - 140, - 300, - ], - Array [ - "lineTo", - 141, - 300, - ], - Array [ - "lineTo", - 142, - 300, - ], - Array [ - "lineTo", - 143, - 300, - ], - Array [ - "lineTo", - 144, - 300, - ], - Array [ - "lineTo", - 145, - 300, - ], - Array [ - "lineTo", - 146, - 300, - ], - Array [ - "lineTo", - 147, - 300, - ], - Array [ - "lineTo", - 148, - 300, - ], - Array [ - "lineTo", - 149, - 300, - ], - Array [ - "lineTo", - 150, - 300, - ], - Array [ - "lineTo", - 151, - 300, - ], - Array [ - "lineTo", - 152, - 300, - ], - Array [ - "lineTo", - 151, - 299.23809519968927, - ], - Array [ - "lineTo", - 150, - 295.99999990314245, - ], - Array [ - "lineTo", - 149, - 288.5714281350374, - ], - Array [ - "lineTo", - 148, - 275.2380944788456, - ], - Array [ - "lineTo", - 147, - 254.2857125401497, - ], - Array [ - "lineTo", - 146, - 225.52380859851837, - ], - Array [ - "lineTo", - 145, - 190.66666066646576, - ], - Array [ - "lineTo", - 144, - 152.1904706954956, - ], - Array [ - "lineTo", - 143, - 113.5237991809845, - ], - Array [ - "lineTo", - 142, - 78.09523344039917, - ], - Array [ - "lineTo", - 141, - 48.571425676345825, - ], - Array [ - "lineTo", - 140, - 26.666665077209473, - ], - Array [ - "lineTo", - 139, - 12.57142424583435, - ], - Array [ - "lineTo", - 138, - 4.571431875228882, - ], - Array [ - "lineTo", - 137, - 0.952380895614624, - ], - Array [ - "lineTo", - 136, - 0, - ], - Array [ - "lineTo", - 135, - 0, - ], - Array [ - "lineTo", - 134, - 0, - ], - Array [ - "lineTo", - 133, - 0, - ], - Array [ - "lineTo", - 132, - 0, - ], - Array [ - "lineTo", - 131, - 0, - ], - Array [ - "lineTo", - 130, - 0, - ], - Array [ - "lineTo", - 129, - 0, - ], - Array [ - "lineTo", - 128, - 0, - ], - Array [ - "lineTo", - 127, - 0, - ], - Array [ - "lineTo", - 126, - 0, - ], - Array [ - "lineTo", - 125, - 0, - ], - Array [ - "lineTo", - 124, - 0, - ], - Array [ - "lineTo", - 123, - 0, - ], - Array [ - "lineTo", - 122, - 0, - ], - Array [ - "lineTo", - 121, - 0, - ], - Array [ - "lineTo", - 120, - 0, - ], - Array [ - "lineTo", - 119, - 0, - ], - Array [ - "lineTo", - 118, - 0, - ], - Array [ - "lineTo", - 117, - 0, - ], - Array [ - "lineTo", - 116, - 0, - ], - Array [ - "lineTo", - 115, - 0, - ], - Array [ - "lineTo", - 114, - 0, - ], - Array [ - "lineTo", - 113, - 0, - ], - Array [ - "lineTo", - 112, - 0, - ], - Array [ - "lineTo", - 111, - 0, - ], - Array [ - "lineTo", - 110, - 0, - ], - Array [ - "lineTo", - 109, - 0, - ], - Array [ - "lineTo", - 108, - 0, - ], - Array [ - "lineTo", - 107, - 0, - ], - Array [ - "lineTo", - 106, - 0, - ], - Array [ - "lineTo", - 105, - 0, - ], - Array [ - "lineTo", - 104, - 0, - ], - Array [ - "lineTo", - 103, - 0, - ], - Array [ - "lineTo", - 102, - 0, - ], - Array [ - "lineTo", - 101, - 0, - ], - Array [ - "lineTo", - 100, - 0, - ], - Array [ - "lineTo", - 99, - 0, - ], - Array [ - "lineTo", - 98, - 0, - ], - Array [ - "lineTo", - 97, - 0, - ], - Array [ - "lineTo", - 96, - 0, - ], - Array [ - "lineTo", - 95, - 0, - ], - Array [ - "lineTo", - 94, - 0, - ], - Array [ - "lineTo", - 93, - 0, - ], - Array [ - "lineTo", - 92, - 0, - ], - Array [ - "lineTo", - 91, - 0, - ], - Array [ - "lineTo", - 90, - 0, - ], - Array [ - "lineTo", - 89, - 0, - ], - Array [ - "lineTo", - 88, - 0, - ], - Array [ - "lineTo", - 87, - 0, - ], - Array [ - "lineTo", - 86, - 0, - ], - Array [ - "lineTo", - 85, - 0, - ], - Array [ - "lineTo", - 84, - 1.3333261013031006, - ], - Array [ - "lineTo", - 83, - 5.714285373687744, - ], - Array [ - "lineTo", - 82, - 14.857149124145508, - ], - Array [ - "lineTo", - 81, - 30.47618865966797, - ], - Array [ - "lineTo", - 80, - 54.28571105003357, - ], - Array [ - "lineTo", - 79, - 85.33333539962769, - ], - Array [ - "lineTo", - 78, - 121.90475463867188, - ], - Array [ - "lineTo", - 77, - 160.95238029956818, - ], - Array [ - "lineTo", - 76, - 199.04761612415314, - ], - Array [ - "lineTo", - 75, - 232.76190161705017, - ], - Array [ - "lineTo", - 74, - 259.99999791383743, - ], - Array [ - "lineTo", - 73, - 279.0476180613041, - ], - Array [ - "lineTo", - 72, - 290.8571429550648, - ], - Array [ - "lineTo", - 71, - 297.14285703375936, - ], - Array [ - "lineTo", - 70, - 299.61904759984463, - ], - Array [ - "closePath", - ], - Array [ - "fill", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - "#ffe900", - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - "#6200a4", - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - "#12bc00", - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - "#0060df", - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - "#ff9400", - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - "#45a1ff", - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 83.88888888888889, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 106.11111111111111, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 128.33333333333331, - 0, - 10, - 300, - ], - Array [ - "set globalCompositeOperation", - "lighter", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - "#d7d7db", - ], - Array [ - "beginPath", - ], - Array [ - "moveTo", - 70, - 300, - ], - Array [ - "lineTo", - 71, - 300, - ], - Array [ - "lineTo", - 72, - 300, - ], - Array [ - "lineTo", - 73, - 300, - ], - Array [ - "lineTo", - 74, - 300, - ], - Array [ - "lineTo", - 75, - 300, - ], - Array [ - "lineTo", - 76, - 300, - ], - Array [ - "lineTo", - 77, - 300, - ], - Array [ - "lineTo", - 78, - 300, - ], - Array [ - "lineTo", - 79, - 300, - ], - Array [ - "lineTo", - 80, - 300, - ], - Array [ - "lineTo", - 81, - 300, - ], - Array [ - "lineTo", - 82, - 300, - ], - Array [ - "lineTo", - 83, - 300, - ], - Array [ - "lineTo", - 84, - 300, - ], - Array [ - "lineTo", - 85, - 300, - ], - Array [ - "lineTo", - 86, - 300, - ], - Array [ - "lineTo", - 87, - 300, - ], - Array [ - "lineTo", - 88, - 300, - ], - Array [ - "lineTo", - 89, - 300, - ], - Array [ - "lineTo", - 90, - 300, - ], - Array [ - "lineTo", - 91, - 300, - ], - Array [ - "lineTo", - 92, - 300, - ], - Array [ - "lineTo", - 93, - 300, - ], - Array [ - "lineTo", - 94, - 300, - ], - Array [ - "lineTo", - 95, - 300, - ], - Array [ - "lineTo", - 96, - 300, - ], - Array [ - "lineTo", - 97, - 300, - ], - Array [ - "lineTo", - 98, - 300, - ], - Array [ - "lineTo", - 99, - 300, - ], - Array [ - "lineTo", - 100, - 300, - ], - Array [ - "lineTo", - 101, - 300, - ], - Array [ - "lineTo", - 102, - 300, - ], - Array [ - "lineTo", - 103, - 300, - ], - Array [ - "lineTo", - 104, - 300, - ], - Array [ - "lineTo", - 105, - 300, - ], - Array [ - "lineTo", - 106, - 300, - ], - Array [ - "lineTo", - 107, - 300, - ], - Array [ - "lineTo", - 108, - 300, - ], - Array [ - "lineTo", - 109, - 300, - ], - Array [ - "lineTo", - 110, - 300, - ], - Array [ - "lineTo", - 111, - 300, - ], - Array [ - "lineTo", - 112, - 300, - ], - Array [ - "lineTo", - 113, - 300, - ], - Array [ - "lineTo", - 114, - 300, - ], - Array [ - "lineTo", - 115, - 300, - ], - Array [ - "lineTo", - 116, - 300, - ], - Array [ - "lineTo", - 117, - 300, - ], - Array [ - "lineTo", - 118, - 300, - ], - Array [ - "lineTo", - 119, - 300, - ], - Array [ - "lineTo", - 120, - 300, - ], - Array [ - "lineTo", - 121, - 300, - ], - Array [ - "lineTo", - 122, - 300, - ], - Array [ - "lineTo", - 123, - 300, - ], - Array [ - "lineTo", - 124, - 300, - ], - Array [ - "lineTo", - 125, - 300, - ], - Array [ - "lineTo", - 126, - 300, - ], - Array [ - "lineTo", - 127, - 300, - ], - Array [ - "lineTo", - 128, - 300, - ], - Array [ - "lineTo", - 129, - 300, - ], - Array [ - "lineTo", - 130, - 300, - ], - Array [ - "lineTo", - 131, - 300, - ], - Array [ - "lineTo", - 132, - 300, - ], - Array [ - "lineTo", - 133, - 300, - ], - Array [ - "lineTo", - 134, - 300, - ], - Array [ - "lineTo", - 135, - 300, - ], - Array [ - "lineTo", - 136, - 300, - ], - Array [ - "lineTo", - 137, - 300, - ], - Array [ - "lineTo", - 138, - 300, - ], - Array [ - "lineTo", - 139, - 300, - ], - Array [ - "lineTo", - 140, - 300, - ], - Array [ - "lineTo", - 141, - 300, - ], - Array [ - "lineTo", - 142, - 300, - ], - Array [ - "lineTo", - 143, - 300, - ], - Array [ - "lineTo", - 144, - 300, - ], - Array [ - "lineTo", - 145, - 300, - ], - Array [ - "lineTo", - 146, - 300, - ], - Array [ - "lineTo", - 147, - 300, - ], - Array [ - "lineTo", - 148, - 300, - ], - Array [ - "lineTo", - 149, - 300, - ], - Array [ - "lineTo", - 150, - 300, - ], - Array [ - "lineTo", - 151, - 300, - ], - Array [ - "lineTo", - 152, - 300, - ], - Array [ - "lineTo", - 151, - 299.23809519968927, - ], - Array [ - "lineTo", - 150, - 295.99999990314245, - ], - Array [ - "lineTo", - 149, - 288.5714281350374, - ], - Array [ - "lineTo", - 148, - 275.2380944788456, - ], - Array [ - "lineTo", - 147, - 254.2857125401497, - ], - Array [ - "lineTo", - 146, - 225.52380859851837, - ], - Array [ - "lineTo", - 145, - 190.66666066646576, - ], - Array [ - "lineTo", - 144, - 152.1904706954956, - ], - Array [ - "lineTo", - 143, - 113.5237991809845, - ], - Array [ - "lineTo", - 142, - 78.09523344039917, - ], - Array [ - "lineTo", - 141, - 48.571425676345825, - ], - Array [ - "lineTo", - 140, - 26.666665077209473, - ], - Array [ - "lineTo", - 139, - 12.57142424583435, - ], - Array [ - "lineTo", - 138, - 4.571431875228882, - ], - Array [ - "lineTo", - 137, - 0.952380895614624, - ], - Array [ - "lineTo", - 136, - 0, - ], - Array [ - "lineTo", - 135, - 0, - ], - Array [ - "lineTo", - 134, - 0, - ], - Array [ - "lineTo", - 133, - 0, - ], - Array [ - "lineTo", - 132, - 0, - ], - Array [ - "lineTo", - 131, - 0, - ], - Array [ - "lineTo", - 130, - 0, - ], - Array [ - "lineTo", - 129, - 0, - ], - Array [ - "lineTo", - 128, - 0, - ], - Array [ - "lineTo", - 127, - 0, - ], - Array [ - "lineTo", - 126, - 0, - ], - Array [ - "lineTo", - 125, - 0, - ], - Array [ - "lineTo", - 124, - 0, - ], - Array [ - "lineTo", - 123, - 0, - ], - Array [ - "lineTo", - 122, - 0, - ], - Array [ - "lineTo", - 121, - 0, - ], - Array [ - "lineTo", - 120, - 0, - ], - Array [ - "lineTo", - 119, - 0, - ], - Array [ - "lineTo", - 118, - 0, - ], - Array [ - "lineTo", - 117, - 0, - ], - Array [ - "lineTo", - 116, - 0, - ], - Array [ - "lineTo", - 115, - 0, - ], - Array [ - "lineTo", - 114, - 0, - ], - Array [ - "lineTo", - 113, - 0, - ], - Array [ - "lineTo", - 112, - 0, - ], - Array [ - "lineTo", - 111, - 0, - ], - Array [ - "lineTo", - 110, - 0, - ], - Array [ - "lineTo", - 109, - 0, - ], - Array [ - "lineTo", - 108, - 0, - ], - Array [ - "lineTo", - 107, - 0, - ], - Array [ - "lineTo", - 106, - 0, - ], - Array [ - "lineTo", - 105, - 0, - ], - Array [ - "lineTo", - 104, - 0, - ], - Array [ - "lineTo", - 103, - 0, - ], - Array [ - "lineTo", - 102, - 0, - ], - Array [ - "lineTo", - 101, - 0, - ], - Array [ - "lineTo", - 100, - 0, - ], - Array [ - "lineTo", - 99, - 0, - ], - Array [ - "lineTo", - 98, - 0, - ], - Array [ - "lineTo", - 97, - 0, - ], - Array [ - "lineTo", - 96, - 0, - ], - Array [ - "lineTo", - 95, - 0, - ], - Array [ - "lineTo", - 94, - 0, - ], - Array [ - "lineTo", - 93, - 0, - ], - Array [ - "lineTo", - 92, - 0, - ], - Array [ - "lineTo", - 91, - 0, - ], - Array [ - "lineTo", - 90, - 0, - ], - Array [ - "lineTo", - 89, - 0, - ], - Array [ - "lineTo", - 88, - 0, - ], - Array [ - "lineTo", - 87, - 0, - ], - Array [ - "lineTo", - 86, - 0, - ], - Array [ - "lineTo", - 85, - 0, - ], - Array [ - "lineTo", - 84, - 1.3333261013031006, - ], - Array [ - "lineTo", - 83, - 5.714285373687744, - ], - Array [ - "lineTo", - 82, - 14.857149124145508, - ], - Array [ - "lineTo", - 81, - 30.47618865966797, - ], - Array [ - "lineTo", - 80, - 54.28571105003357, - ], - Array [ - "lineTo", - 79, - 85.33333539962769, - ], - Array [ - "lineTo", - 78, - 121.90475463867188, - ], - Array [ - "lineTo", - 77, - 160.95238029956818, - ], - Array [ - "lineTo", - 76, - 199.04761612415314, - ], - Array [ - "lineTo", - 75, - 232.76190161705017, - ], - Array [ - "lineTo", - 74, - 259.99999791383743, - ], - Array [ - "lineTo", - 73, - 279.0476180613041, - ], - Array [ - "lineTo", - 72, - 290.8571429550648, - ], - Array [ - "lineTo", - 71, - 297.14285703375936, - ], - Array [ - "lineTo", - 70, - 299.61904759984463, - ], - Array [ - "closePath", - ], - Array [ - "fill", - ], - Array [ - "set fillStyle", - "#d7d7db60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - "#ffe900", - ], - Array [ - "set fillStyle", - "#ffe90060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - "#6200a4", - ], - Array [ - "set fillStyle", - "#6200a460", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - "#12bc00", - ], - Array [ - "set fillStyle", - "#12bc0060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - "#0060df", - ], - Array [ - "set fillStyle", - "#0060df60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - "#ff9400", - ], - Array [ - "set fillStyle", - "#ff940060", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - "#45a1ff", - ], - Array [ - "set fillStyle", - "#45a1ff60", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - "transparent", - ], - Array [ - "set fillStyle", - Object {}, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 83.88888888888889, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 106.11111111111111, - 0, - 10, - 300, - ], - Array [ - "set fillStyle", - "#003eaa", - ], - Array [ - "fillRect", - 128.33333333333331, - 0, - 10, - 300, - ], - Array [ - "clearRect", - 0, - 0, - 200, - 300, - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "clearRect", - 0, - 0, - 200, - 300, - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "clearRect", - 0, - 0, - 200, - 300, - ], - Array [ - "scale", - 1, - 1, - ], - Array [ - "scale", - 1, - 1, - ], -] -`; diff --git a/src/test/store/active-tab.test.js b/src/test/store/active-tab.test.js index b2c0102518..1b985e6bc0 100644 --- a/src/test/store/active-tab.test.js +++ b/src/test/store/active-tab.test.js @@ -293,7 +293,7 @@ describe('finalizeProfileView', function () { tabID: null, }); - // Check if we have the 'cpu-categories' as the timelineType + // Check if we have the 'categories' as the timelineType expect(getTimelineType(getState())).toBe('category'); }); }); diff --git a/src/test/store/profile-view.test.js b/src/test/store/profile-view.test.js index a66f4e8845..6d9658d924 100644 --- a/src/test/store/profile-view.test.js +++ b/src/test/store/profile-view.test.js @@ -18,6 +18,7 @@ import { getProfileWithJsAllocations, addActiveTabInformationToProfile, getProfileWithEventDelays, + getProfileWithThreadCPUDelta, } from '../fixtures/profiles/processed-profile'; import { getEmptyThread, @@ -48,6 +49,7 @@ import { import { ensureExists } from '../../utils/flow'; import { getCallNodeIndexFromPath, + processCounter, type BreakdownByCategory, } from '../../profile-logic/profile-data'; @@ -440,12 +442,12 @@ describe('actions/ProfileView', function () { it('can switch back to the thread, which remembers the last viewed panel', function () { const profile = getNetworkTrackProfile(); const { dispatch, getState } = storeWithProfile(profile); - dispatch(App.changeSelectedTab('flame-graph')); + dispatch(App.changeSelectedTab('marker-table')); expect(UrlStateSelectors.getSelectedThreadIndexes(getState())).toEqual( new Set([0]) ); expect(UrlStateSelectors.getSelectedTab(getState())).toEqual( - 'flame-graph' + 'marker-table' ); dispatch(ProfileView.selectTrack(networkTrack, 'none')); expect(UrlStateSelectors.getSelectedThreadIndexes(getState())).toEqual( @@ -459,7 +461,7 @@ describe('actions/ProfileView', function () { new Set([0]) ); expect(UrlStateSelectors.getSelectedTab(getState())).toEqual( - 'flame-graph' + 'marker-table' ); }); }); @@ -518,9 +520,15 @@ describe('actions/ProfileView', function () { expect(UrlStateSelectors.getSelectedTab(getState())).toEqual( 'calltree' ); + // The thread with the memory track doesn't have samples, so switch to + // a tab that exists even for tables without samples. + dispatch(App.changeSelectedTab('marker-table')); + expect(UrlStateSelectors.getSelectedTab(getState())).toEqual( + 'marker-table' + ); dispatch(ProfileView.selectTrack(memoryTrackReference, 'none')); expect(UrlStateSelectors.getSelectedTab(getState())).toEqual( - 'calltree' + 'marker-table' ); }); }); @@ -3117,13 +3125,25 @@ describe('counter selectors', function () { const counterB = getCounterForThread(thread, threadIndex); profile.counters = [counterA, counterB]; const { getState, dispatch } = storeWithProfile(profile); - return { getState, dispatch, counterA, counterB }; + const processedCounterA = processCounter(counterA); + const processedCounterB = processCounter(counterB); + return { + getState, + dispatch, + counterA, + processedCounterA, + processedCounterB, + }; } it('can get the counters', function () { - const { counterA, counterB, getState } = setup(); - expect(getCounterSelectors(0).getCounter(getState())).toBe(counterA); - expect(getCounterSelectors(1).getCounter(getState())).toBe(counterB); + const { processedCounterA, processedCounterB, getState } = setup(); + expect(getCounterSelectors(0).getCounter(getState())).toStrictEqual( + processedCounterA + ); + expect(getCounterSelectors(1).getCounter(getState())).toStrictEqual( + processedCounterB + ); }); it('can get the counter description', function () { @@ -3138,22 +3158,6 @@ describe('counter selectors', function () { expect(getCounterSelectors(0).getPid(getState())).toBe(0); }); - it('can get the commited range filtered counters', function () { - const { getState, dispatch } = setup(); - // The range includes the sample just before and the sample just after the selection - // range. - dispatch(ProfileView.commitRange(3.5, 5.5)); - const originalCounter = getCounterSelectors(0).getCounter(getState()); - expect(originalCounter.sampleGroups[0].samples.time).toEqual([ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - ]); - - const filteredCounter = getCounterSelectors( - 0 - ).getCommittedRangeFilteredCounter(getState()); - expect(filteredCounter.sampleGroups[0].samples.time).toEqual([3, 4, 5, 6]); - }); - it('can accumulate samples', function () { const { getState, counterA } = setup(); counterA.sampleGroups[0].samples.count = [ @@ -3722,13 +3726,23 @@ describe('mouseTimePosition', function () { }); describe('timeline type', function () { - it('should default to the category view', () => { + it('should use the cpu-category view when CPU usage is provided', () => { + const profile = getProfileWithThreadCPUDelta([[1, 2, 1]]); + const { getState } = storeWithProfile(profile); + expect(UrlStateSelectors.getTimelineType(getState())).toEqual( + 'cpu-category' + ); + }); + + it('should use the category view when cpu is not provided', () => { const { profile } = getProfileFromTextSamples('A'); + + // Load the store after mutating the profile. const { getState } = storeWithProfile(profile); expect(UrlStateSelectors.getTimelineType(getState())).toEqual('category'); }); - it('should use the stack height view when using an imported profile', () => { + it('should use the stack height view when category and cpu is not provided', () => { const { profile } = getProfileFromTextSamples('A'); delete profile.meta.categories; diff --git a/src/test/store/useful-tabs.test.js b/src/test/store/useful-tabs.test.js index f93e91ca38..477283585e 100644 --- a/src/test/store/useful-tabs.test.js +++ b/src/test/store/useful-tabs.test.js @@ -7,6 +7,7 @@ import { selectedThreadSelectors } from '../../selectors/per-thread'; import { storeWithProfile } from '../fixtures/stores'; import { + getMarkerTableProfile, getProfileFromTextSamples, getProfileWithMarkers, getNetworkMarkers, @@ -33,9 +34,6 @@ describe('getUsefulTabs', function () { const profile = getProfileWithMarkers(getNetworkMarkers()); const { getState } = storeWithProfile(profile); expect(selectedThreadSelectors.getUsefulTabs(getState())).toEqual([ - 'calltree', - 'flame-graph', - 'stack-chart', 'marker-chart', 'marker-table', 'network-chart', @@ -46,9 +44,6 @@ describe('getUsefulTabs', function () { const profile = getProfileWithJsTracerEvents([['A', 0, 10]]); const { getState } = storeWithProfile(profile); expect(selectedThreadSelectors.getUsefulTabs(getState())).toEqual([ - 'calltree', - 'flame-graph', - 'stack-chart', 'marker-chart', 'marker-table', 'js-tracer', @@ -97,12 +92,27 @@ describe('getUsefulTabs', function () { // Check the tabs and make sure that the network chart is there. expect(selectedThreadSelectors.getUsefulTabs(getState())).toEqual([ - 'calltree', - 'flame-graph', - 'stack-chart', 'marker-chart', 'marker-table', 'network-chart', ]); }); + + it('hides sample related tabs when there is no sample data in the profile', function () { + const profile = getMarkerTableProfile(); + const { getState } = storeWithProfile(profile); + expect(selectedThreadSelectors.getUsefulTabs(getState())).toEqual([ + 'marker-chart', + 'marker-table', + ]); + }); + + it('hides sample related tabs when samples contain only the (root) frame', function () { + const { profile } = getProfileFromTextSamples('(root)'); + const { getState } = storeWithProfile(profile); + expect(selectedThreadSelectors.getUsefulTabs(getState())).toEqual([ + 'marker-chart', + 'marker-table', + ]); + }); }); diff --git a/src/test/unit/__snapshots__/window-console.test.js.snap b/src/test/unit/__snapshots__/window-console.test.js.snap index 24f2fe5fe1..e9b91bf42a 100644 --- a/src/test/unit/__snapshots__/window-console.test.js.snap +++ b/src/test/unit/__snapshots__/window-console.test.js.snap @@ -33,6 +33,7 @@ Array [ %cwindow.actions%c - All the actions that can be dispatched to change the state. %cwindow.experimental%c - The object that holds flags of all the experimental features. %cwindow.togglePseudoLocalization%c - Enable pseudo localizations by passing \\"accented\\" or \\"bidi\\" to this function, or disable using no parameters. +%cwindow.toggleTimelineType%c - Toggle timeline graph type by passing \\"cpu-category\\", \\"category\\", or \\"stack\\". The profile format is documented here: %chttps://github.com/firefox-devtools/profiler/blob/main/docs-developer/processed-profile-format.md%c @@ -61,6 +62,8 @@ The CallTree class's source code is available here: "", "font-weight: bold;", "", + "font-weight: bold;", + "", "font-style: italic; text-decoration: underline;", "", "font-style: italic; text-decoration: underline;", diff --git a/src/test/url-handling.test.js b/src/test/url-handling.test.js index 3118b6c6b5..ae455de173 100644 --- a/src/test/url-handling.test.js +++ b/src/test/url-handling.test.js @@ -1339,6 +1339,67 @@ describe('url upgrading', function () { }); }); + describe('version 7: change default timeline type', function () { + it('removes the explicit cpu-category from the url', function () { + const { getState } = _getStoreWithURL({ + pathname: '/public/e71ce9584da34298627fb66ac7f2f245ba5edbf5/calltree/', + search: '?timelineType=cpu-category', + v: 6, + }); + + expect(urlStateSelectors.getTimelineType(getState())).toBe( + 'cpu-category' + ); + + const newUrl = new URL( + urlFromState(urlStateSelectors.getUrlState(getState())), + 'https://profiler.firefox.com' + ); + const query = queryString.parse(newUrl.search.substr(1), { + arrayFormat: 'bracket', + }); + expect(query.timelineType).toBeFalsy(); + }); + + it('add an explicit category from the url', function () { + const { getState } = _getStoreWithURL({ + pathname: '/public/e71ce9584da34298627fb66ac7f2f245ba5edbf5/calltree/', + search: '', + v: 6, + }); + + expect(urlStateSelectors.getTimelineType(getState())).toBe('category'); + + const newUrl = new URL( + urlFromState(urlStateSelectors.getUrlState(getState())), + 'https://profiler.firefox.com' + ); + const query = queryString.parse(newUrl.search.substr(1), { + arrayFormat: 'bracket', + }); + expect(query.timelineType).toBe('category'); + }); + + it('keeps stack category the same', function () { + const { getState } = _getStoreWithURL({ + pathname: '/public/e71ce9584da34298627fb66ac7f2f245ba5edbf5/calltree/', + search: '?timelineType=stack', + v: 6, + }); + + expect(urlStateSelectors.getTimelineType(getState())).toBe('stack'); + + const newUrl = new URL( + urlFromState(urlStateSelectors.getUrlState(getState())), + 'https://profiler.firefox.com' + ); + const query = queryString.parse(newUrl.search.substr(1), { + arrayFormat: 'bracket', + }); + expect(query.timelineType).toBe('stack'); + }); + }); + // More general checks it("won't run if the current version is specified", function () { const { getState } = _getStoreWithURL({ @@ -1555,7 +1616,7 @@ describe('urlFromState', function () { '/public/1ecd7a421948995171a4bb483b7bcc8e1868cc57/calltree'; const newUrlState = stateFromLocation({ pathname: pathname, - search: '', + search: `?v=${CURRENT_URL_VERSION}`, hash: '', }); expect(urlFromState(newUrlState)).toEqual( diff --git a/src/utils/window-console.js b/src/utils/window-console.js index ddbceaee0a..040c32b7fb 100644 --- a/src/utils/window-console.js +++ b/src/utils/window-console.js @@ -127,6 +127,26 @@ export function addDataToWindowObject( } }; + target.toggleTimelineType = function (timelineType?: string) { + if ( + timelineType !== 'cpu-category' && + timelineType !== 'category' && + timelineType !== 'stack' + ) { + console.log(stripIndent` + ❗ The timeline type "${timelineType}" is unknown. + 💡 Valid types are: "cpu-category", "category", or "stack". + Please try again 😊 + `); + return; + } + + dispatch(actions.changeTimelineType(timelineType)); + console.log(stripIndent` + ✅ The timeline type "${timelineType}" is now enabled for the timeline. + `); + }; + target.getState = getState; target.selectors = selectorsForConsole; target.dispatch = dispatch; @@ -183,6 +203,7 @@ export function logFriendlyPreamble() { %cwindow.actions%c - All the actions that can be dispatched to change the state. %cwindow.experimental%c - The object that holds flags of all the experimental features. %cwindow.togglePseudoLocalization%c - Enable pseudo localizations by passing "accented" or "bidi" to this function, or disable using no parameters. + %cwindow.toggleTimelineType%c - Toggle timeline graph type by passing "cpu-category", "category", or "stack". The profile format is documented here: %chttps://github.com/firefox-devtools/profiler/blob/main/docs-developer/processed-profile-format.md%c @@ -223,6 +244,9 @@ export function logFriendlyPreamble() { // "window.togglePseudoLocalization" bold, reset, + // "window.toggleTimelineType" + bold, + reset, // "processed-profile-format.md" link, reset, diff --git a/yarn.lock b/yarn.lock index 7c5e0d1c23..5dcc2f6258 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1091,10 +1091,10 @@ "@codemirror/view" "^6.0.0" crelt "^1.0.5" -"@codemirror/state@^6.0.0", "@codemirror/state@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.0.1.tgz#a1994f14c49e2f77cb9e26aef35f63a8b3707c6c" - integrity sha512-6vYgaXc4KjSY0BUfSVDJooGcoswg/RJZpq/ZGjsUYmY0KN1lmB8u03nv+jiG1ncUV5qoggyxFT5AGD5Ak+5Zrw== +"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.1.0.tgz#c0f1d80f61908c9dcf5e2a3fe931e9dd78f3df8a" + integrity sha512-qbUr94DZTe6/V1VS7LDLz11rM/1t/nJxR1El4I6UaxDEdc0aZZvq6JCLJWiRmUf95NRAnDH6fhXn+PWp9wGCIg== "@codemirror/view@^6.0.0", "@codemirror/view@^6.0.2": version "6.0.2" @@ -1309,12 +1309,12 @@ terminal-link "^2.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^28.0.2": - version "28.0.2" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.0.2.tgz#08c30df6a8d07eafea0aef9fb222c5e26d72e613" - integrity sha512-YVDJZjd4izeTDkij00vHHAymNXQ6WWsdChFRK86qck6Jpr3DCL5W3Is3vslviRlP+bLuMYRLbdp98amMvqudhA== +"@jest/schemas@^28.0.2", "@jest/schemas@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" + integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== dependencies: - "@sinclair/typebox" "^0.23.3" + "@sinclair/typebox" "^0.24.1" "@jest/source-map@^28.1.2": version "28.1.2" @@ -1619,10 +1619,10 @@ dot "^1.1.1" svgo "^1.1.1" -"@sinclair/typebox@^0.23.3": - version "0.23.5" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d" - integrity sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg== +"@sinclair/typebox@^0.24.1": + version "0.24.20" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.20.tgz#11a657875de6008622d53f56e063a6347c51a6dd" + integrity sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ== "@sindresorhus/is@^0.14.0": version "0.14.0" @@ -1662,10 +1662,10 @@ dependencies: defer-to-connect "^2.0.0" -"@testing-library/dom@^8.11.1", "@testing-library/dom@^8.14.0", "@testing-library/dom@^8.5.0": - version "8.14.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.14.0.tgz#c9830a21006d87b9ef6e1aae306cf49b0283e28e" - integrity sha512-m8FOdUo77iMTwVRCyzWcqxlEIk+GnopbrRI15a0EaLbpZSCinIVI4kSQzWhkShK83GogvEFJSsHF3Ws0z1vrqA== +"@testing-library/dom@^8.11.1", "@testing-library/dom@^8.16.0", "@testing-library/dom@^8.5.0": + version "8.16.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.16.0.tgz#d6fc50250aed17b1035ca1bd64655e342db3936a" + integrity sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -3231,9 +3231,9 @@ bubble-stream-error@~0.0.1: integrity sha1-VeuGhG7PJmBeiWqi8aMbPJ3My2I= buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer-xor@^1.0.3: version "1.0.3" @@ -4037,10 +4037,10 @@ core-js@^2.5.3: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== -core-js@^3.0.0, core-js@^3.23.3: - version "3.23.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.3.tgz#3b977612b15da6da0c9cc4aec487e8d24f371112" - integrity sha512-oAKwkj9xcWNBAvGbT//WiCdOMpb9XQG92/Fe3ABFM/R16BsHgePG00mFOgKf7IsCtfj8tA1kHtf/VwErhriz5Q== +core-js@^3.0.0, core-js@^3.23.5: + version "3.23.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.5.tgz#1f82b0de5eece800827a2f59d597509c67650475" + integrity sha512-7Vh11tujtAZy82da4duVreQysIoO2EvVrur7y6IzZkH1IHPSekuDi8Vuw1+YKjkbfWLRD7Nc9ICQ/sIUDutcyg== core-util-is@~1.0.0: version "1.0.2" @@ -4625,11 +4625,6 @@ diff-sequences@^26.6.2: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== - diff-sequences@^28.1.1: version "28.1.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" @@ -5094,10 +5089,10 @@ eslint-plugin-jest-formatting@^3.1.0: resolved "https://registry.yarnpkg.com/eslint-plugin-jest-formatting/-/eslint-plugin-jest-formatting-3.1.0.tgz#b26dd5a40f432b642dcc880021a771bb1c93dcd2" integrity sha512-XyysraZ1JSgGbLSDxjj5HzKKh0glgWf+7CkqxbTqb7zEhW7X2WHo5SBQ8cGhnszKN+2Lj3/oevBlHNbHezoc/A== -eslint-plugin-jest@^26.5.3: - version "26.5.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.5.3.tgz#a3ceeaf4a757878342b8b00eca92379b246e5505" - integrity sha512-sICclUqJQnR1bFRZGLN2jnSVsYOsmPYYnroGCIMVSvTS3y8XR3yjzy1EcTQmk6typ5pRgyIWzbjqxK6cZHEZuQ== +eslint-plugin-jest@^26.6.0: + version "26.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.6.0.tgz#546804fa42da75d7d58d4d3b278d5186abd3f6c0" + integrity sha512-f8n46/97ZFdU4KqeQYqO8AEVGIhHWvkpgNBWHH3jrM28/y8llnbf3IjfIKv6p2pZIMinK1PCqbbROxs9Eud02Q== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -5236,10 +5231,10 @@ eslint@^6.0.1: text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^8.18.0: - version "8.18.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.18.0.tgz#78d565d16c993d0b73968c523c0446b13da784fd" - integrity sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA== +eslint@^8.19.0: + version "8.19.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.19.0.tgz#7342a3cbc4fbc5c106a1eefe0fd0b50b6b1a7d28" + integrity sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw== dependencies: "@eslint/eslintrc" "^1.3.0" "@humanwhocodes/config-array" "^0.9.2" @@ -7517,25 +7512,15 @@ jest-diff@^26.0.0: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-diff@^27.2.5: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-diff@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.1.tgz#1a3eedfd81ae79810931c63a1d0f201b9120106c" - integrity sha512-/MUUxeR2fHbqHoMMiffe/Afm+U8U4olFRJ0hiVG2lZatPJcnGxx292ustVu7bULhjV65IYMxRdploAKLbcrsyg== +jest-diff@^28.0.0, jest-diff@^28.1.1: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" + integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== dependencies: chalk "^4.0.0" diff-sequences "^28.1.1" jest-get-type "^28.0.2" - pretty-format "^28.1.1" + pretty-format "^28.1.3" jest-docblock@^28.1.1: version "28.1.1" @@ -7581,25 +7566,20 @@ jest-environment-node@^28.1.2: jest-mock "^28.1.1" jest-util "^28.1.1" -jest-extended@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-2.0.0.tgz#8a6cef7369c2ef774bd966279a30cd73abe408dd" - integrity sha512-6AgjJQVaBEKGSK3FH90kOiRUWJsbzn9NWtW0pjGkAFIdH0oPilfkV/gHPJdVvJeBiqT3jMHw8TUg9pUGC1azDg== +jest-extended@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-2.1.0.tgz#fcb2322caba200a50c037980acc3af0695bcefac" + integrity sha512-4vuDKqLSUZq1DZPvgBZOQ+P08nhZ3W21IlpY5j3EPN6+Q5HaTRfNeMS09Zvknh6Z73cj+csrfgPRQYYEq4V+QA== dependencies: - jest-diff "^27.2.5" - jest-get-type "^27.0.6" + jest-diff "^28.0.0" + jest-get-type "^28.0.0" jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-get-type@^27.0.6, jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - -jest-get-type@^28.0.2: +jest-get-type@^28.0.0, jest-get-type@^28.0.2: version "28.0.2" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== @@ -10833,7 +10813,7 @@ pretty-format@^26.0.0, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^27.0.2, pretty-format@^27.5.1: +pretty-format@^27.0.2: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== @@ -10842,12 +10822,12 @@ pretty-format@^27.0.2, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.1.tgz#f731530394e0f7fcd95aba6b43c50e02d86b95cb" - integrity sha512-wwJbVTGFHeucr5Jw2bQ9P+VYHyLdAqedFLEkdQUVaBF/eiidDwH5OpilINq4mEfhbCjLnirt6HTTDhv1HaTIQw== +pretty-format@^28.1.1, pretty-format@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" + integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== dependencies: - "@jest/schemas" "^28.0.2" + "@jest/schemas" "^28.1.3" ansi-regex "^5.0.1" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -11119,10 +11099,10 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" -react-intersection-observer@^9.3.4: - version "9.3.4" - resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.3.4.tgz#b38a346887f938a2e44bbf4c82a907fb4add675d" - integrity sha512-8L0NpqldlILWE9qurRvPJDx3fgwj5gPUJbtZq860wDdHyR1IU52DhhAVN6rmo3TqHF0CgCA50mIT4SHtBSbKLw== +react-intersection-observer@^9.3.5: + version "9.3.5" + resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.3.5.tgz#df97584c1ef1549a47d4af6380db2fb4b76d7bba" + integrity sha512-TiJXVUapzAaIrZCAMBLjyWvwGYNGm0Xpkcwm3NY23b9PsJEBavul0hRFmrwc/LOmBUA/8TlkjCj7lCvjM0q1Hg== react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" @@ -12279,9 +12259,9 @@ source-map-support@0.5.13: source-map "^0.6.0" source-map-support@~0.5.12: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -13013,9 +12993,9 @@ terser-webpack-plugin@^1.4.3: worker-farm "^1.7.0" terser@^4.1.2, terser@^4.6.3: - version "4.8.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + version "4.8.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" + integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== dependencies: commander "^2.20.0" source-map "~0.6.1"