Merge "[ui] Rename frontendLocalState -> timeline" into main am: a4488671e3 am: a7c053b64a am: 68877c47f1

Original change: https://android-review.googlesource.com/c/platform/external/perfetto/+/2873835

Change-Id: I7537103771c3e0aef852e3028b493fa50dfb92ab
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/ui/src/common/canvas_utils.ts b/ui/src/common/canvas_utils.ts
index ad78f96..2f56b5f 100644
--- a/ui/src/common/canvas_utils.ts
+++ b/ui/src/common/canvas_utils.ts
@@ -151,7 +151,7 @@
   y -= 10;
 
   // Ensure the box is on screen:
-  const endPx = globals.frontendLocalState.visibleTimeScale.pxSpan.end;
+  const endPx = globals.timeline.visibleTimeScale.pxSpan.end;
   if (x + width > endPx) {
     x -= x + width - endPx;
   }
diff --git a/ui/src/common/track_helper.ts b/ui/src/common/track_helper.ts
index 933ce45..3a11596 100644
--- a/ui/src/common/track_helper.ts
+++ b/ui/src/common/track_helper.ts
@@ -57,7 +57,7 @@
   }
 
   requestDataForCurrentTime(): void {
-    const currentTimeSpan = globals.frontendLocalState.visibleTimeSpan;
+    const currentTimeSpan = globals.timeline.visibleTimeSpan;
     const currentResolution = globals.getCurResolution();
     this.requestData(currentTimeSpan, currentResolution);
   }
diff --git a/ui/src/controller/ftrace_controller.ts b/ui/src/controller/ftrace_controller.ts
index e596ff4..e4f13cc 100644
--- a/ui/src/controller/ftrace_controller.ts
+++ b/ui/src/controller/ftrace_controller.ts
@@ -49,7 +49,7 @@
 
   run() {
     if (this.shouldUpdate()) {
-      this.oldSpan = globals.frontendLocalState.visibleWindowTime;
+      this.oldSpan = globals.timeline.visibleWindowTime;
       this.oldFtraceFilter = globals.state.ftraceFilter;
       this.oldPagination = globals.state.ftracePagination;
       if (globals.state.ftracePagination.count > 0) {
@@ -69,7 +69,7 @@
 
   private shouldUpdate(): boolean {
     // Has the visible window moved?
-    const visibleWindow = globals.frontendLocalState.visibleWindowTime;
+    const visibleWindow = globals.timeline.visibleWindowTime;
     if (!this.oldSpan.equals(visibleWindow)) {
       return true;
     }
diff --git a/ui/src/frontend/base_counter_track.ts b/ui/src/frontend/base_counter_track.ts
index 5081cb7..079fb1a 100644
--- a/ui/src/frontend/base_counter_track.ts
+++ b/ui/src/frontend/base_counter_track.ts
@@ -180,7 +180,7 @@
     const {
       visibleTimeScale: timeScale,
       visibleWindowTime: vizTime,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
 
     {
       const windowSizePx = Math.max(1, timeScale.pxSpan.delta);
@@ -383,7 +383,7 @@
     const data = this.counters;
     if (data === undefined) return;
     this.mousePos = pos;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const time = visibleTimeScale.pxToHpTime(pos.x);
 
     let values = data.lastValues;
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index 95855cf..30778c2 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -332,7 +332,7 @@
     const {
       visibleTimeScale: timeScale,
       visibleWindowTime: vizTime,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
 
     {
       const windowSizePx = Math.max(1, timeScale.pxSpan.delta);
@@ -816,7 +816,7 @@
     }
 
     for (const slice of this.incomplete) {
-      const visibleTimeScale = globals.frontendLocalState.visibleTimeScale;
+      const visibleTimeScale = globals.timeline.visibleTimeScale;
       const startPx = CROP_INCOMPLETE_SLICE_FLAG.get() ?
           visibleTimeScale.timeToPx(slice.startNsQ) :
           slice.x;
@@ -973,7 +973,7 @@
       windowSpan,
       visibleTimeScale,
       visibleTimeSpan,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
 
     const pxEnd = windowSpan.end;
     const left = Math.max(visibleTimeScale.timeToPx(tStart), 0);
diff --git a/ui/src/frontend/drag/drag_strategy.ts b/ui/src/frontend/drag/drag_strategy.ts
index 7c6a1ca..869921e 100644
--- a/ui/src/frontend/drag/drag_strategy.ts
+++ b/ui/src/frontend/drag/drag_strategy.ts
@@ -28,7 +28,7 @@
 
   protected updateGlobals(tStart: HighPrecisionTime, tEnd: HighPrecisionTime) {
     const vizTime = new HighPrecisionTimeSpan(tStart, tEnd);
-    globals.frontendLocalState.updateVisibleTime(vizTime);
+    globals.timeline.updateVisibleTime(vizTime);
     raf.scheduleRedraw();
   }
 }
diff --git a/ui/src/frontend/flow_events_renderer.ts b/ui/src/frontend/flow_events_renderer.ts
index 12ce93c..8d2da34 100644
--- a/ui/src/frontend/flow_events_renderer.ts
+++ b/ui/src/frontend/flow_events_renderer.ts
@@ -135,7 +135,7 @@
   }
 
   private getXCoordinate(ts: time): number {
-    return globals.frontendLocalState.visibleTimeScale.timeToPx(ts);
+    return globals.timeline.visibleTimeScale.timeToPx(ts);
   }
 
   private getSliceRect(args: FlowEventsRendererArgs, point: FlowPoint):
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index a649966..6394672 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -136,7 +136,7 @@
  * State that is shared between several frontend components, but not the
  * controller. This state is updated at 60fps.
  */
-export class FrontendLocalState {
+export class Timeline {
   private visibleWindow = new TimeWindow();
   private _timeScale = this.visibleWindow.createTimeScale(0, 0);
   private _windowSpan = PxSpan.ZERO;
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 435bf99..845c0d5 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -51,7 +51,7 @@
 
 import {Analytics, initAnalytics} from './analytics';
 import {BottomTabList} from './bottom_tab';
-import {FrontendLocalState} from './frontend_local_state';
+import {Timeline} from './frontend_local_state';
 import {Router} from './router';
 import {horizontalScrollToTs} from './scroll_helper';
 import {ServiceWorkerController} from './service_worker_controller';
@@ -248,7 +248,7 @@
   private _testing = false;
   private _dispatch?: Dispatch = undefined;
   private _store?: Store<State>;
-  private _frontendLocalState?: FrontendLocalState = undefined;
+  private _timeline?: Timeline = undefined;
   private _serviceWorkerController?: ServiceWorkerController = undefined;
   private _logging?: Analytics = undefined;
   private _isInternalUser: boolean|undefined = undefined;
@@ -315,7 +315,7 @@
     this._router = router;
     this._store = createStore(initialState);
     this._cmdManager = cmdManager;
-    this._frontendLocalState = new FrontendLocalState();
+    this._timeline = new Timeline();
 
     setPerfHooks(
         () => this.state.perfDebug,
@@ -379,8 +379,8 @@
     }
   }
 
-  get frontendLocalState() {
-    return assertExists(this._frontendLocalState);
+  get timeline() {
+    return assertExists(this._timeline);
   }
 
   get logging() {
@@ -599,7 +599,7 @@
     // levels. Logic: each zoom level represents a delta of 0.1 * (visible
     // window span). Therefore, zooming out by six levels is 1.1^6 ~= 2.
     // Similarily, zooming in six levels is 0.9^6 ~= 0.5.
-    const timeScale = this.frontendLocalState.visibleTimeScale;
+    const timeScale = this.timeline.visibleTimeScale;
     // TODO(b/186265930): Remove once fixed:
     if (timeScale.pxSpan.delta === 0) {
       console.error(`b/186265930: Bad pxToSec suppressed`);
@@ -656,7 +656,7 @@
   resetForTesting() {
     this._dispatch = undefined;
     this._store = undefined;
-    this._frontendLocalState = undefined;
+    this._timeline = undefined;
     this._serviceWorkerController = undefined;
 
     // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
diff --git a/ui/src/frontend/gridline_helper.ts b/ui/src/frontend/gridline_helper.ts
index 04c24b3..60d959f 100644
--- a/ui/src/frontend/gridline_helper.ts
+++ b/ui/src/frontend/gridline_helper.ts
@@ -178,7 +178,7 @@
 // Gets the timescale associated with the current visible window.
 export function timeScaleForVisibleWindow(
     startPx: number, endPx: number): TimeScale {
-  return globals.frontendLocalState.getTimeScale(startPx, endPx);
+  return globals.timeline.getTimeScale(startPx, endPx);
 }
 
 export function drawGridLines(
@@ -188,7 +188,7 @@
   ctx.strokeStyle = TRACK_BORDER_COLOR;
   ctx.lineWidth = 1;
 
-  const span = globals.frontendLocalState.visibleTimeSpan;
+  const span = globals.timeline.visibleTimeSpan;
   if (width > TRACK_SHELL_WIDTH && span.duration > 0n) {
     const maxMajorTicks = getMaxMajorTicks(width - TRACK_SHELL_WIDTH);
     const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, width);
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index 581f61c..2ab0455 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -71,7 +71,7 @@
     // recently than the visible time handled by the frontend @ 60fps,
     // update it. This typically happens when restoring the state from a
     // permalink.
-    globals.frontendLocalState.mergeState(state.frontendLocalState);
+    globals.timeline.mergeState(state.frontendLocalState);
 
     // Only redraw if something other than the frontendLocalState changed.
     let key: keyof State;
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index e27f83c..386167f 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -80,7 +80,7 @@
     return true;
   }
   if (down && 'escape' === key) {
-    globals.frontendLocalState.deselectArea();
+    globals.timeline.deselectArea();
     globals.makeSelection(Actions.deselect({}));
     globals.dispatch(Actions.removeNote({id: '0'}));
     return true;
diff --git a/ui/src/frontend/logs_panel.ts b/ui/src/frontend/logs_panel.ts
index c05b881..cd17638 100644
--- a/ui/src/frontend/logs_panel.ts
+++ b/ui/src/frontend/logs_panel.ts
@@ -93,7 +93,7 @@
       firstVisibleLogTs,
       lastVisibleLogTs,
     } = this.bounds;
-    const vis = globals.frontendLocalState.visibleWindowTime;
+    const vis = globals.timeline.visibleWindowTime;
 
     const visibleLogSpan =
         new HighPrecisionTimeSpan(firstVisibleLogTs, lastVisibleLogTs);
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index 1d7589c..b3a3d13 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -124,8 +124,8 @@
     ctx.rect(TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
     ctx.clip();
 
-    const span = globals.frontendLocalState.visibleTimeSpan;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const span = globals.timeline.visibleTimeSpan;
+    const {visibleTimeScale} = globals.timeline;
     if (size.width > TRACK_SHELL_WIDTH && span.duration > 0n) {
       const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
       const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
@@ -267,7 +267,7 @@
 
   private onClick(x: number, _: number) {
     if (x < 0) return;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const timestamp = visibleTimeScale.pxToHpTime(x).toTime();
     for (const note of Object.values(globals.state.notes)) {
       if (this.hoveredX && this.mouseOverNote(this.hoveredX, note)) {
@@ -285,7 +285,7 @@
   }
 
   private mouseOverNote(x: number, note: AreaNote|Note): boolean {
-    const timeScale = globals.frontendLocalState.visibleTimeScale;
+    const timeScale = globals.timeline.visibleTimeScale;
     const noteX = timeScale.timeToPx(getStartTimestamp(note));
     if (note.noteType === 'AREA') {
       const noteArea = globals.state.areas[note.areaId];
diff --git a/ui/src/frontend/overview_timeline_panel.ts b/ui/src/frontend/overview_timeline_panel.ts
index edda1d3..12ffc28 100644
--- a/ui/src/frontend/overview_timeline_panel.ts
+++ b/ui/src/frontend/overview_timeline_panel.ts
@@ -226,7 +226,7 @@
   }
 
   private static extractBounds(timeScale: TimeScale): [number, number] {
-    const vizTime = globals.frontendLocalState.visibleWindowTime;
+    const vizTime = globals.timeline.visibleWindowTime;
     return [
       Math.floor(timeScale.hpTimeToPx(vizTime.start)),
       Math.ceil(timeScale.hpTimeToPx(vizTime.end)),
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 5497d8e..4365896 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -121,10 +121,9 @@
   // This finds the tracks covered by the in-progress area selection. When
   // editing areaY is not set, so this will not be used.
   handleAreaSelection() {
-    const area = globals.frontendLocalState.selectedArea;
-    if (area === undefined ||
-        globals.frontendLocalState.areaY.start === undefined ||
-        globals.frontendLocalState.areaY.end === undefined ||
+    const area = globals.timeline.selectedArea;
+    if (area === undefined || globals.timeline.areaY.start === undefined ||
+        globals.timeline.areaY.end === undefined ||
         this.panelInfos.length === 0) {
       return;
     }
@@ -133,22 +132,20 @@
     const panelContainerTop = this.panelInfos[0].y;
     const panelContainerBottom = this.panelInfos[this.panelInfos.length - 1].y +
         this.panelInfos[this.panelInfos.length - 1].height;
-    if (globals.frontendLocalState.areaY.start + TOPBAR_HEIGHT <
-            panelContainerTop ||
-        globals.frontendLocalState.areaY.start + TOPBAR_HEIGHT >
-            panelContainerBottom) {
+    if (globals.timeline.areaY.start + TOPBAR_HEIGHT < panelContainerTop ||
+        globals.timeline.areaY.start + TOPBAR_HEIGHT > panelContainerBottom) {
       return;
     }
 
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
 
     // The Y value is given from the top of the pan and zoom region, we want it
     // from the top of the panel container. The parent offset corrects that.
     const panels = this.getPanelsInRegion(
         visibleTimeScale.timeToPx(area.start),
         visibleTimeScale.timeToPx(area.end),
-        globals.frontendLocalState.areaY.start + TOPBAR_HEIGHT,
-        globals.frontendLocalState.areaY.end + TOPBAR_HEIGHT);
+        globals.timeline.areaY.start + TOPBAR_HEIGHT,
+        globals.timeline.areaY.end + TOPBAR_HEIGHT);
     // Get the track ids from the panels.
     const tracks = [];
     for (const panel of panels) {
@@ -167,7 +164,7 @@
         }
       }
     }
-    globals.frontendLocalState.selectArea(area.start, area.end, tracks);
+    globals.timeline.selectArea(area.start, area.end, tracks);
   }
 
   constructor(vnode: m.CVnode<Attrs>) {
@@ -288,7 +285,7 @@
       this.updateCanvasDimensions();
       this.repositionCanvas();
       if (this.attrs.kind === 'TRACKS') {
-        globals.frontendLocalState.updateLocalLimits(
+        globals.timeline.updateLocalLimits(
             0, this.parentWidth - TRACK_SHELL_WIDTH);
       }
       this.redrawCanvas();
@@ -430,10 +427,9 @@
   // the whole canvas rather than per panel.
   private drawTopLayerOnCanvas() {
     if (!this.ctx) return;
-    const area = globals.frontendLocalState.selectedArea;
-    if (area === undefined ||
-        globals.frontendLocalState.areaY.start === undefined ||
-        globals.frontendLocalState.areaY.end === undefined) {
+    const area = globals.timeline.selectedArea;
+    if (area === undefined || globals.timeline.areaY.start === undefined ||
+        globals.timeline.areaY.end === undefined) {
       return;
     }
     if (this.panelInfos.length === 0 || area.tracks.length === 0) return;
@@ -458,7 +454,7 @@
       return;
     }
 
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const startX = visibleTimeScale.timeToPx(area.start);
     const endX = visibleTimeScale.timeToPx(area.end);
     // To align with where to draw on the canvas subtract the first panel Y.
diff --git a/ui/src/frontend/scroll_helper.ts b/ui/src/frontend/scroll_helper.ts
index b514b61..4df9b14 100644
--- a/ui/src/frontend/scroll_helper.ts
+++ b/ui/src/frontend/scroll_helper.ts
@@ -27,14 +27,14 @@
 // center |ts|, keeping the same zoom level.
 export function horizontalScrollToTs(ts: time) {
   const time = HighPrecisionTime.fromTime(ts);
-  const visibleWindow = globals.frontendLocalState.visibleWindowTime;
+  const visibleWindow = globals.timeline.visibleWindowTime;
   if (!visibleWindow.contains(time)) {
     // TODO(hjd): This is an ugly jump, we should do a smooth pan instead.
     const halfDuration = visibleWindow.duration.divide(2);
     const newStart = time.sub(halfDuration);
     const newWindow = new HighPrecisionTimeSpan(
         newStart, newStart.add(visibleWindow.duration));
-    globals.frontendLocalState.updateVisibleTime(newWindow);
+    globals.timeline.updateVisibleTime(newWindow);
   }
 }
 
@@ -52,7 +52,7 @@
 // - Otherwise, preserve the zoom range.
 export function focusHorizontalRange(
     start: time, end: time, viewPercentage?: number) {
-  const visible = globals.frontendLocalState.visibleWindowTime;
+  const visible = globals.timeline.visibleWindowTime;
   const trace = globals.stateTraceTime();
   const select = HighPrecisionTimeSpan.fromTime(start, end);
 
@@ -68,13 +68,13 @@
     const paddingPercentage = 1.0 - viewPercentage;
     const paddingTime = select.duration.multiply(paddingPercentage);
     const halfPaddingTime = paddingTime.divide(2);
-    globals.frontendLocalState.updateVisibleTime(select.pad(halfPaddingTime));
+    globals.timeline.updateVisibleTime(select.pad(halfPaddingTime));
     return;
   }
   // If the range is too large to fit on the current zoom level, resize.
   if (select.duration.gt(visible.duration.multiply(0.5))) {
     const paddedRange = select.pad(select.duration.multiply(2));
-    globals.frontendLocalState.updateVisibleTime(paddedRange);
+    globals.timeline.updateVisibleTime(paddedRange);
     return;
   }
   // Calculate the new visible window preserving the zoom level.
@@ -99,9 +99,9 @@
   // level.
   if (view.start.eq(visible.start) && view.end.eq(visible.end)) {
     const padded = select.pad(select.duration.multiply(2));
-    globals.frontendLocalState.updateVisibleTime(padded);
+    globals.timeline.updateVisibleTime(padded);
   } else {
-    globals.frontendLocalState.updateVisibleTime(view);
+    globals.timeline.updateVisibleTime(view);
   }
 }
 
diff --git a/ui/src/frontend/slice_track.ts b/ui/src/frontend/slice_track.ts
index 685ff8c..21885d0 100644
--- a/ui/src/frontend/slice_track.ts
+++ b/ui/src/frontend/slice_track.ts
@@ -82,7 +82,7 @@
     const data = this.data;
     if (data === undefined) return;  // Can't possibly draw anything.
 
-    const {visibleTimeSpan, visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeSpan, visibleTimeScale} = globals.timeline;
 
     // If the cached trace slices don't fully cover the visible time range,
     // show a gray rectangle with a "Loading..." label.
@@ -262,7 +262,7 @@
     if (data === undefined) return;
     const {
       visibleTimeScale: timeScale,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     if (y < TRACK_PADDING) return;
     const instantWidthTime = timeScale.pxDeltaToDuration(HALF_CHEVRON_WIDTH_PX);
     const t = timeScale.pxToHpTime(x);
@@ -293,7 +293,7 @@
   }
 
   getEndTimeIfInComplete(start: time): time {
-    const {visibleTimeScale, visibleWindowTime} = globals.frontendLocalState;
+    const {visibleTimeScale, visibleWindowTime} = globals.timeline;
 
     let end = visibleWindowTime.end.toTime('ceil');
     if (CROP_INCOMPLETE_SLICE_FLAG.get()) {
@@ -349,7 +349,7 @@
       windowSpan,
       visibleTimeScale,
       visibleTimeSpan,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
 
     const pxEnd = windowSpan.end;
     const left = Math.max(visibleTimeScale.timeToPx(tStart), 0);
diff --git a/ui/src/frontend/tickmark_panel.ts b/ui/src/frontend/tickmark_panel.ts
index 9390c7f..5a363cc 100644
--- a/ui/src/frontend/tickmark_panel.ts
+++ b/ui/src/frontend/tickmark_panel.ts
@@ -33,7 +33,7 @@
   }
 
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
 
     ctx.fillStyle = '#999';
     ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
@@ -43,7 +43,7 @@
     ctx.rect(TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
     ctx.clip();
 
-    const visibleSpan = globals.frontendLocalState.visibleTimeSpan;
+    const visibleSpan = globals.timeline.visibleTimeSpan;
     if (size.width > TRACK_SHELL_WIDTH && visibleSpan.duration > 0n) {
       const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
       const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
diff --git a/ui/src/frontend/time_axis_panel.ts b/ui/src/frontend/time_axis_panel.ts
index f4372af..6238c8d 100644
--- a/ui/src/frontend/time_axis_panel.ts
+++ b/ui/src/frontend/time_axis_panel.ts
@@ -66,7 +66,7 @@
     ctx.clip();
 
     // Draw time axis.
-    const span = globals.frontendLocalState.visibleTimeSpan;
+    const span = globals.timeline.visibleTimeSpan;
     if (size.width > TRACK_SHELL_WIDTH && span.duration > 0n) {
       const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
       const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
diff --git a/ui/src/frontend/time_selection_panel.ts b/ui/src/frontend/time_selection_panel.ts
index 8137c6f..0b61594 100644
--- a/ui/src/frontend/time_selection_panel.ts
+++ b/ui/src/frontend/time_selection_panel.ts
@@ -144,7 +144,7 @@
     ctx.rect(TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
     ctx.clip();
 
-    const span = globals.frontendLocalState.visibleTimeSpan;
+    const span = globals.timeline.visibleTimeSpan;
     if (size.width > TRACK_SHELL_WIDTH && span.duration > 0n) {
       const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
       const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
@@ -159,7 +159,7 @@
       }
     }
 
-    const localArea = globals.frontendLocalState.selectedArea;
+    const localArea = globals.timeline.selectedArea;
     const selection = globals.state.currentSelection;
     if (localArea !== undefined) {
       const start = Time.min(localArea.start, localArea.end);
@@ -190,7 +190,7 @@
   }
 
   renderHover(ctx: CanvasRenderingContext2D, size: PanelSize, ts: time) {
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const xPos = TRACK_SHELL_WIDTH + Math.floor(visibleTimeScale.timeToPx(ts));
     const domainTime = globals.toDomainTime(ts);
     const label = stringifyTimestamp(domainTime);
@@ -200,7 +200,7 @@
   renderSpan(
       ctx: CanvasRenderingContext2D, size: PanelSize,
       span: Span<time, duration>) {
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const xLeft = visibleTimeScale.timeToPx(span.start);
     const xRight = visibleTimeScale.timeToPx(span.end);
     const label = renderDuration(span.duration);
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts
index 6b69607..04ae41e 100644
--- a/ui/src/frontend/track_group_panel.ts
+++ b/ui/src/frontend/track_group_panel.ts
@@ -204,7 +204,7 @@
   }
 
   highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const selection = globals.state.currentSelection;
     if (!selection || selection.kind !== 'AREA') return;
     const selectedArea = globals.state.areas[selection.areaId];
@@ -244,7 +244,7 @@
 
     this.highlightIfTrackSelected(ctx, size);
 
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     // Draw vertical line when hovering on the notes panel.
     if (globals.state.hoveredNoteTimestamp !== -1n) {
       drawVerticalLineAtTime(
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index afd58c6..b4fcda4 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -416,7 +416,7 @@
   }
 
   highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const selection = globals.state.currentSelection;
     const trackState = this.trackState;
     if (!selection || selection.kind !== 'AREA' || trackState === undefined) {
@@ -453,7 +453,7 @@
 
     this.highlightIfTrackSelected(ctx, size);
 
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     // Draw vertical line when hovering on the notes panel.
     if (globals.state.hoveredNoteTimestamp !== -1n) {
       drawVerticalLineAtTime(
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 57aa0a3..071c968 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -51,10 +51,10 @@
   if (selection !== null && selection.kind === 'AREA') {
     // If frontend selectedArea exists then we are in the process of editing the
     // time range and need to use that value instead.
-    const area = globals.frontendLocalState.selectedArea ?
-        globals.frontendLocalState.selectedArea :
+    const area = globals.timeline.selectedArea ?
+        globals.timeline.selectedArea :
         globals.state.areas[selection.areaId];
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const start = visibleTimeScale.timeToPx(area.start);
     const end = visibleTimeScale.timeToPx(area.end);
     const startDrag = mousePos - TRACK_SHELL_WIDTH;
@@ -94,10 +94,10 @@
   private keepCurrentSelection = false;
 
   oncreate(vnode: m.CVnodeDOM) {
-    const frontendLocalState = globals.frontendLocalState;
+    const timeline = globals.timeline;
     const updateDimensions = () => {
       const rect = vnode.dom.getBoundingClientRect();
-      frontendLocalState.updateLocalLimits(
+      timeline.updateLocalLimits(
           0, rect.width - TRACK_SHELL_WIDTH - getScrollbarWidth());
     };
 
@@ -120,11 +120,11 @@
       onPanned: (pannedPx: number) => {
         const {
           visibleTimeScale,
-        } = globals.frontendLocalState;
+        } = globals.timeline;
 
         this.keepCurrentSelection = true;
         const tDelta = visibleTimeScale.pxDeltaToDuration(pannedPx);
-        frontendLocalState.panVisibleWindow(tDelta);
+        timeline.panVisibleWindow(tDelta);
 
         // If the user has panned they no longer need the hint.
         localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
@@ -136,7 +136,7 @@
         const zoomPx = zoomedPositionPx - TRACK_SHELL_WIDTH;
         const rect = vnode.dom.getBoundingClientRect();
         const centerPoint = zoomPx / (rect.width - TRACK_SHELL_WIDTH);
-        frontendLocalState.zoomVisibleWindow(1 - zoomRatio, centerPoint);
+        timeline.zoomVisibleWindow(1 - zoomRatio, centerPoint);
         raf.scheduleRedraw();
       },
       editSelection: (currentPx: number) => {
@@ -150,13 +150,13 @@
           currentY: number,
           editing: boolean) => {
         const traceTime = globals.state.traceTime;
-        const {visibleTimeScale} = frontendLocalState;
+        const {visibleTimeScale} = timeline;
         this.keepCurrentSelection = true;
         if (editing) {
           const selection = globals.state.currentSelection;
           if (selection !== null && selection.kind === 'AREA') {
-            const area = globals.frontendLocalState.selectedArea ?
-                globals.frontendLocalState.selectedArea :
+            const area = globals.timeline.selectedArea ?
+                globals.timeline.selectedArea :
                 globals.state.areas[selection.areaId];
             let newTime =
                 visibleTimeScale.pxToHpTime(currentX - TRACK_SHELL_WIDTH)
@@ -175,7 +175,7 @@
             }
             // When editing the time range we always use the saved tracks,
             // since these will not change.
-            frontendLocalState.selectArea(
+            timeline.selectArea(
                 Time.max(Time.min(keepTime, newTime), traceTime.start),
                 Time.min(Time.max(keepTime, newTime), traceTime.end),
                 globals.state.areas[selection.areaId].tracks);
@@ -187,20 +187,20 @@
           const {pxSpan} = visibleTimeScale;
           startPx = clamp(startPx, pxSpan.start, pxSpan.end);
           endPx = clamp(endPx, pxSpan.start, pxSpan.end);
-          frontendLocalState.selectArea(
+          timeline.selectArea(
               visibleTimeScale.pxToHpTime(startPx).toTime('floor'),
               visibleTimeScale.pxToHpTime(endPx).toTime('ceil'),
           );
-          frontendLocalState.areaY.start = dragStartY;
-          frontendLocalState.areaY.end = currentY;
+          timeline.areaY.start = dragStartY;
+          timeline.areaY.end = currentY;
           publishShowPanningHint();
         }
         raf.scheduleRedraw();
       },
       endSelection: (edit: boolean) => {
-        globals.frontendLocalState.areaY.start = undefined;
-        globals.frontendLocalState.areaY.end = undefined;
-        const area = globals.frontendLocalState.selectedArea;
+        globals.timeline.areaY.start = undefined;
+        globals.timeline.areaY.end = undefined;
+        const area = globals.timeline.selectedArea;
         // If we are editing we need to pass the current id through to ensure
         // the marked area with that id is also updated.
         if (edit) {
@@ -214,8 +214,8 @@
         }
         // Now the selection has ended we stored the final selected area in the
         // global state and can remove the in progress selection from the
-        // frontendLocalState.
-        globals.frontendLocalState.deselectArea();
+        // timeline.
+        globals.timeline.deselectArea();
         // Full redraw to color track shell.
         raf.scheduleFullRedraw();
       },
diff --git a/ui/src/tracks/android_log/index.ts b/ui/src/tracks/android_log/index.ts
index 04237b6..b1a31e2 100644
--- a/ui/src/tracks/android_log/index.ts
+++ b/ui/src/tracks/android_log/index.ts
@@ -109,7 +109,7 @@
   }
 
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize): void {
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
 
     const data = this.data();
 
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_link_utils.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_link_utils.ts
index 7209356..df919ce 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_link_utils.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_link_utils.ts
@@ -201,7 +201,7 @@
             verticalScrollToTrack(trackKeys[0], true);
             if (exists(ts) && exists(dur)) {
               focusHorizontalRange(ts, Time.fromRaw(ts + dur), 0.3);
-              globals.frontendLocalState.selectArea(
+              globals.timeline.selectArea(
                   ts, Time.fromRaw(ts + dur), trackKeys);
 
               globals.dispatch(Actions.selectArea({
diff --git a/ui/src/tracks/counter/index.ts b/ui/src/tracks/counter/index.ts
index 6917588..5178321 100644
--- a/ui/src/tracks/counter/index.ts
+++ b/ui/src/tracks/counter/index.ts
@@ -353,7 +353,7 @@
     // TODO: fonts and colors should come from the CSS and not hardcoded here.
     const {
       visibleTimeScale: timeScale,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     const data = this.data;
 
     // Can't possibly draw anything.
@@ -560,7 +560,7 @@
     const data = this.data;
     if (data === undefined) return;
     this.mousePos = pos;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const time = visibleTimeScale.pxToHpTime(pos.x);
 
     let values = data.lastValues;
@@ -587,7 +587,7 @@
   onMouseClick({x}: {x: number}): boolean {
     const data = this.data;
     if (data === undefined) return false;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const time = visibleTimeScale.pxToHpTime(x);
     const [left, right] = searchSegment(data.timestamps, time.toTime());
     if (left === -1) {
diff --git a/ui/src/tracks/cpu_freq/index.ts b/ui/src/tracks/cpu_freq/index.ts
index 06f3087..7d1377b 100644
--- a/ui/src/tracks/cpu_freq/index.ts
+++ b/ui/src/tracks/cpu_freq/index.ts
@@ -294,7 +294,7 @@
     const {
       visibleTimeScale,
       visibleWindowTime,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     const data = this.data();
 
     if (data === undefined || data.timestamps.length === 0) {
@@ -466,7 +466,7 @@
     const data = this.data();
     if (data === undefined) return;
     this.mousePos = pos;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const time = visibleTimeScale.pxToHpTime(pos.x);
 
     const [left, right] = searchSegment(data.timestamps, time.toTime());
diff --git a/ui/src/tracks/cpu_profile/index.ts b/ui/src/tracks/cpu_profile/index.ts
index df0916b..bc4c6bd 100644
--- a/ui/src/tracks/cpu_profile/index.ts
+++ b/ui/src/tracks/cpu_profile/index.ts
@@ -106,7 +106,7 @@
   renderCanvas(ctx: CanvasRenderingContext2D, _size: PanelSize): void {
     const {
       visibleTimeScale: timeScale,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     const data = this.data();
 
     if (data === undefined) return;
@@ -181,7 +181,7 @@
     if (data === undefined) return;
     const {
       visibleTimeScale: timeScale,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     const time = timeScale.pxToHpTime(x);
     const [left, right] = searchSegment(data.tsStarts, time.toTime());
     const index = this.findTimestampIndex(left, timeScale, data, x, y, right);
@@ -198,7 +198,7 @@
     if (data === undefined) return false;
     const {
       visibleTimeScale: timeScale,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
 
     const time = timeScale.pxToHpTime(x);
     const [left, right] = searchSegment(data.tsStarts, time.toTime());
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index 1df393b..90926b7 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -223,7 +223,7 @@
 
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize): void {
     // TODO: fonts and colors should come from the CSS and not hardcoded here.
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const data = this.data();
 
     if (data === undefined) return;  // Can't possibly draw anything.
@@ -246,7 +246,7 @@
       visibleTimeScale,
       visibleTimeSpan,
       visibleWindowTime,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     assertTrue(data.starts.length === data.ends.length);
     assertTrue(data.starts.length === data.utids.length);
 
@@ -425,7 +425,7 @@
     const data = this.data();
     this.mousePos = pos;
     if (data === undefined) return;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     if (pos.y < MARGIN_TOP || pos.y > MARGIN_TOP + RECT_HEIGHT) {
       this.utidHoveredInThisTrack = -1;
       globals.dispatch(Actions.setHoveredUtidAndPid({utid: -1, pid: -1}));
@@ -459,7 +459,7 @@
   onMouseClick({x}: {x: number}) {
     const data = this.data();
     if (data === undefined) return false;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const time = visibleTimeScale.pxToHpTime(x);
     const index = search(data.starts, time.toTime());
     const id = index === -1 ? undefined : data.ids[index];
diff --git a/ui/src/tracks/ftrace/index.ts b/ui/src/tracks/ftrace/index.ts
index 6ae8474..531bad8 100644
--- a/ui/src/tracks/ftrace/index.ts
+++ b/ui/src/tracks/ftrace/index.ts
@@ -93,7 +93,7 @@
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize): void {
     const {
       visibleTimeScale,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
 
     const data = this.data;
 
diff --git a/ui/src/tracks/perf_samples_profile/index.ts b/ui/src/tracks/perf_samples_profile/index.ts
index a78e36d..17734f1 100644
--- a/ui/src/tracks/perf_samples_profile/index.ts
+++ b/ui/src/tracks/perf_samples_profile/index.ts
@@ -103,7 +103,7 @@
   renderCanvas(ctx: CanvasRenderingContext2D, _size: PanelSize): void {
     const {
       visibleTimeScale,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     const data = this.data();
 
     if (data === undefined) return;
@@ -147,7 +147,7 @@
   onMouseMove({x, y}: {x: number, y: number}) {
     const data = this.data();
     if (data === undefined) return;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const time = visibleTimeScale.pxToHpTime(x);
     const [left, right] = searchSegment(data.tsStarts, time.toTime());
     const index =
@@ -163,7 +163,7 @@
   onMouseClick({x, y}: {x: number, y: number}) {
     const data = this.data();
     if (data === undefined) return false;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
 
     const time = visibleTimeScale.pxToHpTime(x);
     const [left, right] = searchSegment(data.tsStarts, time.toTime());
diff --git a/ui/src/tracks/process_summary/process_scheduling_track.ts b/ui/src/tracks/process_summary/process_scheduling_track.ts
index 455cac5..b818d31 100644
--- a/ui/src/tracks/process_summary/process_scheduling_track.ts
+++ b/ui/src/tracks/process_summary/process_scheduling_track.ts
@@ -206,7 +206,7 @@
     const {
       visibleTimeScale,
       visibleTimeSpan,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     const data = this.data();
 
     if (data === undefined) return;  // Can't possibly draw anything.
@@ -288,7 +288,7 @@
 
     const cpuTrackHeight = Math.floor(RECT_HEIGHT / data.maxCpu);
     const cpu = Math.floor((pos.y - MARGIN_TOP) / (cpuTrackHeight + 1));
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const t = visibleTimeScale.pxToHpTime(pos.x).toTime('floor');
 
     const [i, j] = searchRange(data.starts, t, searchEq(data.cpus, cpu));
diff --git a/ui/src/tracks/process_summary/process_summary_track.ts b/ui/src/tracks/process_summary/process_summary_track.ts
index d3b834a..f47e6e9 100644
--- a/ui/src/tracks/process_summary/process_summary_track.ts
+++ b/ui/src/tracks/process_summary/process_summary_track.ts
@@ -156,7 +156,7 @@
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize): void {
     const {
       visibleTimeScale,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     const data = this.data();
     if (data === undefined) return;  // Can't possibly draw anything.
 
@@ -173,7 +173,7 @@
 
   // TODO(dproy): Dedup with CPU slices.
   renderSummary(ctx: CanvasRenderingContext2D, data: Data): void {
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const startPx = 0;
     const bottomY = TRACK_HEIGHT;
 
diff --git a/ui/src/tracks/thread_state/index.ts b/ui/src/tracks/thread_state/index.ts
index 78fa367..c72d61a 100644
--- a/ui/src/tracks/thread_state/index.ts
+++ b/ui/src/tracks/thread_state/index.ts
@@ -195,7 +195,7 @@
     const {
       visibleTimeScale: timeScale,
       visibleTimeSpan,
-    } = globals.frontendLocalState;
+    } = globals.timeline;
     const data = this.data();
     const charWidth = ctx.measureText('dbpqaouk').width / 8;
 
@@ -279,7 +279,7 @@
   onMouseClick({x}: {x: number}) {
     const data = this.data();
     if (data === undefined) return false;
-    const {visibleTimeScale} = globals.frontendLocalState;
+    const {visibleTimeScale} = globals.timeline;
     const time = visibleTimeScale.pxToHpTime(x);
     const index = search(data.starts, time.toTime());
     if (index === -1) return false;