Merge "ui: Remove old console.log" into main
diff --git a/ui/src/frontend/brand.ts b/ui/src/base/brand.ts
similarity index 100%
rename from ui/src/frontend/brand.ts
rename to ui/src/base/brand.ts
diff --git a/ui/src/common/time.ts b/ui/src/base/time.ts
similarity index 83%
rename from ui/src/common/time.ts
rename to ui/src/base/time.ts
index ad598e5..7d4c19a 100644
--- a/ui/src/common/time.ts
+++ b/ui/src/base/time.ts
@@ -12,11 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {BigintMath} from '../base/bigint_math';
-import {assertTrue} from '../base/logging';
-import {Brand} from '../frontend/brand';
-
-import {ColumnType} from './query_result';
+import {BigintMath} from './bigint_math';
+import {Brand} from './brand';
+import {assertTrue} from './logging';
 
 // The |time| type represents trace time in the same units and domain as trace
 // processor (i.e. typically boot time in nanoseconds, but most of the UI should
@@ -60,20 +58,6 @@
     return v as (time | undefined);
   }
 
-  // Throws if the value cannot be reasonably converted to a bigint.
-  // Assumes value is in native time units.
-  static fromSql(value: ColumnType): time {
-    if (typeof value === 'bigint') {
-      return Time.fromRaw(value);
-    } else if (typeof value === 'number') {
-      return Time.fromRaw(BigInt(Math.floor(value)));
-    } else if (value === null) {
-      return Time.ZERO;
-    } else {
-      throw Error(`Refusing to create time from unrelated type ${value}`);
-    }
-  }
-
   // Create a time from seconds.
   // Use this function with caution. Number does not have the full range of time
   // so only use when stricy accuracy isn't required.
@@ -148,20 +132,6 @@
     return BigInt(Math.floor((millis / 1e3) * TIME_UNITS_PER_SEC));
   }
 
-  // Throws if the value cannot be reasonably converted to a bigint.
-  // Assumes value is in nanoseconds.
-  static fromSql(value: ColumnType): duration {
-    if (typeof value === 'bigint') {
-      return value;
-    } else if (typeof value === 'number') {
-      return BigInt(Math.floor(value));
-    } else if (value === null) {
-      return Duration.ZERO;
-    } else {
-      throw Error(`Refusing to create duration from unrelated type ${value}`);
-    }
-  }
-
   // Convert time to seconds as a number.
   // Use this function with caution. It loses precision and is slow.
   static toSeconds(d: duration) {
@@ -340,34 +310,3 @@
         Time.sub(this.start, padding), Time.add(this.end, padding));
   }
 }
-
-export enum TimestampFormat {
-  Timecode = 'timecode',
-  Raw = 'raw',
-  RawLocale = 'rawLocale',
-  Seconds = 'seconds',
-}
-
-let timestampFormatCached: TimestampFormat|undefined;
-
-const TIMESTAMP_FORMAT_KEY = 'timestampFormat';
-const DEFAULT_TIMESTAMP_FORMAT = TimestampFormat.Timecode;
-
-function isTimestampFormat(value: unknown): value is TimestampFormat {
-  return Object.values(TimestampFormat).includes(value as TimestampFormat);
-}
-
-export function timestampFormat(): TimestampFormat {
-  const storedFormat = localStorage.getItem(TIMESTAMP_FORMAT_KEY);
-  if (storedFormat && isTimestampFormat(storedFormat)) {
-    timestampFormatCached = storedFormat;
-  } else {
-    timestampFormatCached = DEFAULT_TIMESTAMP_FORMAT;
-  }
-  return timestampFormatCached;
-}
-
-export function setTimestampFormat(format: TimestampFormat) {
-  timestampFormatCached = format;
-  localStorage.setItem(TIMESTAMP_FORMAT_KEY, format);
-}
diff --git a/ui/src/common/time_unittest.ts b/ui/src/base/time_unittest.ts
similarity index 95%
rename from ui/src/common/time_unittest.ts
rename to ui/src/base/time_unittest.ts
index 08d6c07..2219780 100644
--- a/ui/src/common/time_unittest.ts
+++ b/ui/src/base/time_unittest.ts
@@ -12,23 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {globals} from '../frontend/globals';
-
-import {createEmptyState} from './empty_state';
 import {
-
   Duration,
   Time,
   Timecode,
   TimeSpan,
-} from './time';
-
-beforeAll(() => {
-  globals.initStore(createEmptyState());
-  globals.store.edit((draft) => {
-    draft.traceTime.start = Time.fromRaw(0n);
-  });
-});
+} from '../base/time';
 
 test('Duration.format', () => {
   expect(Duration.format(0n)).toEqual('0s');
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 7afdea6..c1dd361 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -15,6 +15,7 @@
 import {Draft} from 'immer';
 
 import {assertExists, assertTrue, assertUnreachable} from '../base/logging';
+import {duration, time} from '../base/time';
 import {RecordConfig} from '../controller/record_config_types';
 import {
   GenericSliceDetailsTabConfig,
@@ -74,7 +75,6 @@
   UtidToTrackSortKey,
   VisibleState,
 } from './state';
-import {duration, time} from './time';
 
 export const DEBUG_SLICE_TRACK_KIND = 'DebugSliceTrack';
 
diff --git a/ui/src/common/actions_unittest.ts b/ui/src/common/actions_unittest.ts
index 4343acc..261dd2e 100644
--- a/ui/src/common/actions_unittest.ts
+++ b/ui/src/common/actions_unittest.ts
@@ -15,6 +15,7 @@
 import {produce} from 'immer';
 
 import {assertExists} from '../base/logging';
+import {Time} from '../base/time';
 import {SLICE_TRACK_KIND} from '../tracks/chrome_slices';
 import {HEAP_PROFILE_TRACK_KIND} from '../tracks/heap_profile';
 import {
@@ -33,7 +34,6 @@
   TraceUrlSource,
   TrackSortKey,
 } from './state';
-import {Time} from './time';
 
 function fakeTrack(state: State, args: {
   id: string,
diff --git a/ui/src/common/basic_async_track.ts b/ui/src/common/basic_async_track.ts
index 3b9e850..1cc4516 100644
--- a/ui/src/common/basic_async_track.ts
+++ b/ui/src/common/basic_async_track.ts
@@ -14,13 +14,13 @@
 
 import m from 'mithril';
 
+import {duration, Span, Time, time} from '../base/time';
 import {globals} from '../frontend/globals';
 import {PxSpan, TimeScale} from '../frontend/time_scale';
 import {SliceRect} from '../frontend/track';
 import {TrackButtonAttrs} from '../frontend/track_panel';
 import {TrackLike} from '../public';
 
-import {duration, Span, Time, time} from './time';
 import {TrackData} from './track_data';
 
 export {Store} from '../frontend/store';
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index c6d4248..23644e5 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Time} from '../base/time';
 import {createEmptyRecordConfig} from '../controller/record_config_types';
 import {
   Aggregation,
@@ -29,7 +30,6 @@
   State,
   STATE_VERSION,
 } from './state';
-import {Time} from './time';
 
 const AUTOLOAD_STARTED_CONFIG_FLAG = featureFlags.register({
   id: 'autoloadStartedConfig',
diff --git a/ui/src/common/engine.ts b/ui/src/common/engine.ts
index 3b0e1e4..4dc7a7f 100644
--- a/ui/src/common/engine.ts
+++ b/ui/src/common/engine.ts
@@ -15,7 +15,7 @@
 import {defer, Deferred} from '../base/deferred';
 import {Disposable} from '../base/disposable';
 import {assertExists, assertTrue} from '../base/logging';
-import {Span, Time} from '../common/time';
+import {duration, Span, Time, time, TimeSpan} from '../base/time';
 import {
   ComputeMetricArgs,
   ComputeMetricResult,
@@ -36,7 +36,6 @@
   STR,
   WritableQueryResult,
 } from './query_result';
-import {duration, time, TimeSpan} from './time';
 
 import TraceProcessorRpc = perfetto.protos.TraceProcessorRpc;
 import TraceProcessorRpcStream = perfetto.protos.TraceProcessorRpcStream;
diff --git a/ui/src/common/high_precision_time.ts b/ui/src/common/high_precision_time.ts
index c5e4024..a886e8a 100644
--- a/ui/src/common/high_precision_time.ts
+++ b/ui/src/common/high_precision_time.ts
@@ -13,8 +13,7 @@
 // limitations under the License.
 
 import {assertTrue} from '../base/logging';
-
-import {Span, Time, time} from './time';
+import {Span, Time, time} from '../base/time';
 
 export type RoundMode = 'round'|'floor'|'ceil';
 export type Timeish = HighPrecisionTime|time;
diff --git a/ui/src/common/high_precision_time_unittest.ts b/ui/src/common/high_precision_time_unittest.ts
index df22bb6..b4bd7dd 100644
--- a/ui/src/common/high_precision_time_unittest.ts
+++ b/ui/src/common/high_precision_time_unittest.ts
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Time, time} from '../base/time';
+
 import {
   HighPrecisionTime as HPTime,
   HighPrecisionTimeSpan as HPTimeInterval,
 } from './high_precision_time';
-import {Time, time} from './time';
 
 // Quick 'n' dirty function to convert a string to a HPtime
 // Used to make tests more readable
diff --git a/ui/src/common/logs.ts b/ui/src/common/logs.ts
index c79a292..ca4e370 100644
--- a/ui/src/common/logs.ts
+++ b/ui/src/common/logs.ts
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {time} from './time';
+import {time} from '../base/time';
 
 export const LogExistsKey = 'log-exists';
 export const LogBoundsKey = 'log-bounds';
diff --git a/ui/src/common/query_result.ts b/ui/src/common/query_result.ts
index ad5e1ee..0a1875f 100644
--- a/ui/src/common/query_result.ts
+++ b/ui/src/common/query_result.ts
@@ -55,6 +55,7 @@
 import {defer, Deferred} from '../base/deferred';
 import {assertExists, assertFalse, assertTrue} from '../base/logging';
 import {utf8Decode} from '../base/string_utils';
+import {Duration, duration, Time, time} from '../base/time';
 
 export const NUM = 0;
 export const STR = 'str';
@@ -958,3 +959,31 @@
     Promise<QueryResult>&WritableQueryResult {
   return new WaitableQueryResultImpl(errorInfo);
 }
+
+// Throws if the value cannot be reasonably converted to a bigint.
+// Assumes value is in native time units.
+export function timeFromSql(value: ColumnType): time {
+  if (typeof value === 'bigint') {
+    return Time.fromRaw(value);
+  } else if (typeof value === 'number') {
+    return Time.fromRaw(BigInt(Math.floor(value)));
+  } else if (value === null) {
+    return Time.ZERO;
+  } else {
+    throw Error(`Refusing to create time from unrelated type ${value}`);
+  }
+}
+
+// Throws if the value cannot be reasonably converted to a bigint.
+// Assumes value is in nanoseconds.
+export function durationFromSql(value: ColumnType): duration {
+  if (typeof value === 'bigint') {
+    return value;
+  } else if (typeof value === 'number') {
+    return BigInt(Math.floor(value));
+  } else if (value === null) {
+    return Duration.ZERO;
+  } else {
+    throw Error(`Refusing to create duration from unrelated type ${value}`);
+  }
+}
diff --git a/ui/src/common/recordingV2/recording_page_controller.ts b/ui/src/common/recordingV2/recording_page_controller.ts
index c42130c..79bd8b6 100644
--- a/ui/src/common/recordingV2/recording_page_controller.ts
+++ b/ui/src/common/recordingV2/recording_page_controller.ts
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 import {assertExists, assertTrue} from '../../base/logging';
+import {currentDateHourAndMinute} from '../../base/time';
 import {TraceConfig} from '../../core/protos';
 import {raf} from '../../core/raf_scheduler';
 import {globals} from '../../frontend/globals';
@@ -26,7 +27,6 @@
 } from '../../frontend/recording/reset_interface_modal';
 import {Actions} from '../actions';
 import {TRACE_SUFFIX} from '../constants';
-import {currentDateHourAndMinute} from '../time';
 
 import {genTraceConfig} from './recording_config_utils';
 import {RecordingError, showRecordingModal} from './recording_error_handling';
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 8635192..79c1a93 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 import {BigintMath} from '../base/bigint_math';
+import {duration, Time, time} from '../base/time';
 import {RecordConfig} from '../controller/record_config_types';
 import {
   GenericSliceDetailsTabConfigBase,
@@ -25,7 +26,6 @@
 import {TrackTags} from '../public/index';
 
 import {Direction} from './event_set';
-import {duration, Time, time} from './time';
 
 /**
  * A plain js object, holding objects of type |Class| keyed by string id.
diff --git a/ui/src/common/timestamp_format.ts b/ui/src/common/timestamp_format.ts
new file mode 100644
index 0000000..b50eaed
--- /dev/null
+++ b/ui/src/common/timestamp_format.ts
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export enum TimestampFormat {
+  Timecode = 'timecode',
+  Raw = 'raw',
+  RawLocale = 'rawLocale',
+  Seconds = 'seconds',
+}
+
+let timestampFormatCached: TimestampFormat|undefined;
+
+const TIMESTAMP_FORMAT_KEY = 'timestampFormat';
+const DEFAULT_TIMESTAMP_FORMAT = TimestampFormat.Timecode;
+
+function isTimestampFormat(value: unknown): value is TimestampFormat {
+  return Object.values(TimestampFormat).includes(value as TimestampFormat);
+}
+
+export function timestampFormat(): TimestampFormat {
+  const storedFormat = localStorage.getItem(TIMESTAMP_FORMAT_KEY);
+  if (storedFormat && isTimestampFormat(storedFormat)) {
+    timestampFormatCached = storedFormat;
+  } else {
+    timestampFormatCached = DEFAULT_TIMESTAMP_FORMAT;
+  }
+  return timestampFormatCached;
+}
+
+export function setTimestampFormat(format: TimestampFormat) {
+  timestampFormatCached = format;
+  localStorage.setItem(TIMESTAMP_FORMAT_KEY, format);
+}
diff --git a/ui/src/common/track_adapter.ts b/ui/src/common/track_adapter.ts
index c642076..09284b7 100644
--- a/ui/src/common/track_adapter.ts
+++ b/ui/src/common/track_adapter.ts
@@ -15,8 +15,8 @@
 import m from 'mithril';
 
 import {assertExists} from '../base/logging';
+import {duration, Span, time} from '../base/time';
 import {EngineProxy} from '../common/engine';
-import {duration, Span, time} from '../common/time';
 import {PxSpan, TimeScale} from '../frontend/time_scale';
 import {NewTrackArgs, SliceRect} from '../frontend/track';
 import {TrackButtonAttrs} from '../frontend/track_panel';
diff --git a/ui/src/common/track_data.ts b/ui/src/common/track_data.ts
index 29d3967..bb74ccc 100644
--- a/ui/src/common/track_data.ts
+++ b/ui/src/common/track_data.ts
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {duration, time} from './time';
+import {duration, time} from '../base/time';
 
 // TODO(hjd): Refactor into method on TrackController
 export const LIMIT = 10000;
diff --git a/ui/src/controller/aggregation/counter_aggregation_controller.ts b/ui/src/controller/aggregation/counter_aggregation_controller.ts
index 41a47fe..3c3cf15 100644
--- a/ui/src/controller/aggregation/counter_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/counter_aggregation_controller.ts
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Duration} from '../../base/time';
 import {ColumnDef} from '../../common/aggregation_data';
 import {Engine} from '../../common/engine';
 import {Area, Sorting} from '../../common/state';
-import {Duration} from '../../common/time';
 import {globals} from '../../frontend/globals';
 import {Config, COUNTER_TRACK_KIND} from '../../tracks/counter';
 
diff --git a/ui/src/controller/area_selection_handler_unittest.ts b/ui/src/controller/area_selection_handler_unittest.ts
index 6a26bd7..2e7bca1 100644
--- a/ui/src/controller/area_selection_handler_unittest.ts
+++ b/ui/src/controller/area_selection_handler_unittest.ts
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Time} from '../base/time';
 import {createEmptyState} from '../common/empty_state';
 import {AreaById} from '../common/state';
-import {Time} from '../common/time';
 import {globals} from '../frontend/globals';
 
 import {AreaSelectionHandler} from './area_selection_handler';
diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts
index b7289a5..20bdb04 100644
--- a/ui/src/controller/flamegraph_controller.ts
+++ b/ui/src/controller/flamegraph_controller.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Duration, time} from '../base/time';
 import {Actions} from '../common/actions';
 import {Engine} from '../common/engine';
 import {
@@ -27,7 +28,6 @@
 } from '../common/flamegraph_util';
 import {NUM, STR} from '../common/query_result';
 import {CallsiteInfo, FlamegraphState, ProfileType} from '../common/state';
-import {Duration, time} from '../common/time';
 import {FlamegraphDetails, globals} from '../frontend/globals';
 import {publishFlamegraphDetails} from '../frontend/publish';
 import {
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index dd94d55..06f9da9 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Time} from '../base/time';
 import {Engine} from '../common/engine';
 import {featureFlags} from '../common/feature_flags';
 import {LONG, NUM, STR_NULL} from '../common/query_result';
 import {Area} from '../common/state';
-import {Time} from '../common/time';
 import {Flow, globals} from '../frontend/globals';
 import {publishConnectedFlows, publishSelectedFlows} from '../frontend/publish';
 import {asSliceSqlId} from '../frontend/sql_types';
diff --git a/ui/src/controller/ftrace_controller.ts b/ui/src/controller/ftrace_controller.ts
index eee568f..27e600a 100644
--- a/ui/src/controller/ftrace_controller.ts
+++ b/ui/src/controller/ftrace_controller.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Span, Time} from '../base/time';
 import {Engine} from '../common/engine';
 import {
   HighPrecisionTime,
@@ -19,7 +20,6 @@
 } from '../common/high_precision_time';
 import {LONG, NUM, STR, STR_NULL} from '../common/query_result';
 import {FtraceFilterState, Pagination} from '../common/state';
-import {Span, Time} from '../common/time';
 import {FtraceEvent, globals} from '../frontend/globals';
 import {publishFtracePanelData} from '../frontend/publish';
 import {ratelimit} from '../frontend/rate_limiters';
diff --git a/ui/src/controller/logs_controller.ts b/ui/src/controller/logs_controller.ts
index e9a44c3..1a421ac 100644
--- a/ui/src/controller/logs_controller.ts
+++ b/ui/src/controller/logs_controller.ts
@@ -12,6 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {
+  duration,
+  Span,
+  time,
+  Time,
+  TimeSpan,
+} from '../base/time';
 import {Engine} from '../common/engine';
 import {
   LogBounds,
@@ -23,13 +30,6 @@
 import {LONG, LONG_NULL, NUM, STR} from '../common/query_result';
 import {escapeGlob, escapeQuery} from '../common/query_utils';
 import {LogFilteringCriteria} from '../common/state';
-import {
-  duration,
-  Span,
-  time,
-  Time,
-  TimeSpan,
-} from '../common/time';
 import {globals} from '../frontend/globals';
 import {publishTrackData} from '../frontend/publish';
 
diff --git a/ui/src/controller/search_controller.ts b/ui/src/controller/search_controller.ts
index b4b0234..1d1de5e 100644
--- a/ui/src/controller/search_controller.ts
+++ b/ui/src/controller/search_controller.ts
@@ -13,11 +13,6 @@
 // limitations under the License.
 
 import {sqliteString} from '../base/string_utils';
-import {Engine} from '../common/engine';
-import {LONG, NUM, STR} from '../common/query_result';
-import {escapeSearchQuery} from '../common/query_utils';
-import {CurrentSearchResults, SearchSummary} from '../common/search_data';
-import {OmniboxState} from '../common/state';
 import {
   Duration,
   duration,
@@ -25,7 +20,12 @@
   time,
   Time,
   TimeSpan,
-} from '../common/time';
+} from '../base/time';
+import {Engine} from '../common/engine';
+import {LONG, NUM, STR} from '../common/query_result';
+import {escapeSearchQuery} from '../common/query_utils';
+import {CurrentSearchResults, SearchSummary} from '../common/search_data';
+import {OmniboxState} from '../common/state';
 import {globals} from '../frontend/globals';
 import {publishSearch, publishSearchResult} from '../frontend/publish';
 
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index be6a207..b2c8ee5 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -13,17 +13,19 @@
 // limitations under the License.
 
 import {assertTrue} from '../base/logging';
+import {Time, time} from '../base/time';
 import {Args, ArgValue} from '../common/arg_types';
 import {Engine} from '../common/engine';
 import {
+  durationFromSql,
   LONG,
   NUM,
   NUM_NULL,
   STR,
   STR_NULL,
+  timeFromSql,
 } from '../common/query_result';
 import {ChromeSliceSelection} from '../common/state';
-import {Duration, Time, time} from '../common/time';
 import {
   CounterDetails,
   globals,
@@ -180,10 +182,10 @@
         case 'id':
           break;
         case 'ts':
-          ts = Time.fromSql(v);
+          ts = timeFromSql(v);
           break;
         case 'thread_ts':
-          threadTs = Time.fromSql(v);
+          threadTs = timeFromSql(v);
           break;
         case 'absTime':
           if (v) absTime = `${v}`;
@@ -192,10 +194,10 @@
           name = `${v}`;
           break;
         case 'dur':
-          dur = Duration.fromSql(v);
+          dur = durationFromSql(v);
           break;
         case 'thread_dur':
-          threadDur = Duration.fromSql(v);
+          threadDur = durationFromSql(v);
           break;
         case 'category':
         case 'cat':
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 4ae7370..66f75b2 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -15,6 +15,14 @@
 import {BigintMath} from '../base/bigint_math';
 import {assertExists, assertTrue} from '../base/logging';
 import {
+  Duration,
+  duration,
+  Span,
+  time,
+  Time,
+  TimeSpan,
+} from '../base/time';
+import {
   Actions,
   DeferredAction,
 } from '../common/actions';
@@ -47,14 +55,6 @@
   PendingDeeplinkState,
   ProfileType,
 } from '../common/state';
-import {
-  Duration,
-  duration,
-  Span,
-  time,
-  Time,
-  TimeSpan,
-} from '../common/time';
 import {resetEngineWorker, WasmEngineProxy} from '../common/wasm_engine_proxy';
 import {BottomTabList} from '../frontend/bottom_tab';
 import {
diff --git a/ui/src/controller/track_controller.ts b/ui/src/controller/track_controller.ts
index c95b72b..0e441ea 100644
--- a/ui/src/controller/track_controller.ts
+++ b/ui/src/controller/track_controller.ts
@@ -14,16 +14,16 @@
 
 import {BigintMath} from '../base/bigint_math';
 import {assertExists} from '../base/logging';
-import {Engine} from '../common/engine';
-import {Registry} from '../common/registry';
-import {RESOLUTION_DEFAULT, TraceTime, TrackState} from '../common/state';
 import {
   Duration,
   duration,
   time,
   Time,
   TimeSpan,
-} from '../common/time';
+} from '../base/time';
+import {Engine} from '../common/engine';
+import {Registry} from '../common/registry';
+import {RESOLUTION_DEFAULT, TraceTime, TrackState} from '../common/state';
 import {LIMIT, TrackData} from '../common/track_data';
 import {globals} from '../frontend/globals';
 import {publishTrackData} from '../frontend/publish';
diff --git a/ui/src/frontend/aggregation_panel.ts b/ui/src/frontend/aggregation_panel.ts
index 03c7631..d4453d9 100644
--- a/ui/src/frontend/aggregation_panel.ts
+++ b/ui/src/frontend/aggregation_panel.ts
@@ -14,6 +14,7 @@
 
 import m from 'mithril';
 
+import {Duration} from '../base/time';
 import {Actions} from '../common/actions';
 import {
   AggregateData,
@@ -22,7 +23,6 @@
 } from '../common/aggregation_data';
 import {colorForState, textColorForState} from '../common/colorizer';
 import {translateState} from '../common/thread_state';
-import {Duration} from '../common/time';
 
 import {globals} from './globals';
 import {Panel} from './panel';
diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts
index 19fc40c..b11f0df 100644
--- a/ui/src/frontend/app.ts
+++ b/ui/src/frontend/app.ts
@@ -19,16 +19,15 @@
 import {FuzzyFinder} from '../base/fuzzy';
 import {assertExists} from '../base/logging';
 import {undoCommonChatAppReplacements} from '../base/string_utils';
-import {Actions} from '../common/actions';
 import {
   duration,
-  setTimestampFormat,
   Span,
   Time,
   time,
   TimeSpan,
-  TimestampFormat,
-} from '../common/time';
+} from '../base/time';
+import {Actions} from '../common/actions';
+import {setTimestampFormat, TimestampFormat} from '../common/timestamp_format';
 import {raf} from '../core/raf_scheduler';
 import {Command} from '../public';
 
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index f462ebe..46a5f5f 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -13,6 +13,12 @@
 // limitations under the License.
 
 import {assertExists} from '../base/logging';
+import {
+  duration,
+  Span,
+  Time,
+  time,
+} from '../base/time';
 import {Actions} from '../common/actions';
 import {cropText, drawIncompleteSlice} from '../common/canvas_utils';
 import {
@@ -21,12 +27,6 @@
 } from '../common/colorizer';
 import {LONG, NUM} from '../common/query_result';
 import {Selection, SelectionKind} from '../common/state';
-import {
-  duration,
-  Span,
-  Time,
-  time,
-} from '../common/time';
 import {raf} from '../core/raf_scheduler';
 
 import {checkerboardExcept} from './checkerboard';
diff --git a/ui/src/frontend/base_slice_track_unittest.ts b/ui/src/frontend/base_slice_track_unittest.ts
index 09c782a..717010e 100644
--- a/ui/src/frontend/base_slice_track_unittest.ts
+++ b/ui/src/frontend/base_slice_track_unittest.ts
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Time} from '../base/time';
 import {GRAY_COLOR} from '../common/colorizer';
-import {Time} from '../common/time';
 
 import {
   filterVisibleSlicesForTesting as filterVisibleSlices,
diff --git a/ui/src/frontend/chrome_slice_details_tab.ts b/ui/src/frontend/chrome_slice_details_tab.ts
index a118767..3cb4eb5 100644
--- a/ui/src/frontend/chrome_slice_details_tab.ts
+++ b/ui/src/frontend/chrome_slice_details_tab.ts
@@ -14,11 +14,11 @@
 
 import m from 'mithril';
 
+import {duration, Time} from '../base/time';
 import {exists} from '../base/utils';
 import {EngineProxy} from '../common/engine';
 import {runQuery} from '../common/queries';
 import {LONG, LONG_NULL, NUM, STR_NULL} from '../common/query_result';
-import {duration, Time} from '../common/time';
 import {addDebugTrack} from '../tracks/debug/slice_track';
 
 import {
diff --git a/ui/src/frontend/flamegraph_panel.ts b/ui/src/frontend/flamegraph_panel.ts
index e32286f..915bd94 100644
--- a/ui/src/frontend/flamegraph_panel.ts
+++ b/ui/src/frontend/flamegraph_panel.ts
@@ -16,6 +16,7 @@
 
 import {findRef} from '../base/dom_utils';
 import {assertExists, assertTrue} from '../base/logging';
+import {Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {
   ALLOC_SPACE_MEMORY_ALLOCATED_KEY,
@@ -29,7 +30,6 @@
   FlamegraphStateViewingOption,
   ProfileType,
 } from '../common/state';
-import {Time} from '../common/time';
 import {profileType} from '../controller/flamegraph_controller';
 import {raf} from '../core/raf_scheduler';
 
diff --git a/ui/src/frontend/flow_events_renderer.ts b/ui/src/frontend/flow_events_renderer.ts
index 3270d7e..4d49c1a 100644
--- a/ui/src/frontend/flow_events_renderer.ts
+++ b/ui/src/frontend/flow_events_renderer.ts
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {time} from '../common/time';
+import {time} from '../base/time';
 
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {ALL_CATEGORIES, getFlowCategories} from './flow_events_panel';
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index 14ad7f3..d9d36d9 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 import {assertTrue} from '../base/logging';
+import {duration, Span, Time, time, TimeSpan} from '../base/time';
 import {Actions} from '../common/actions';
 import {
   HighPrecisionTime,
@@ -25,7 +26,6 @@
   Timestamped,
   VisibleState,
 } from '../common/state';
-import {duration, Span, Time, time, TimeSpan} from '../common/time';
 import {raf} from '../core/raf_scheduler';
 
 import {globals} from './globals';
diff --git a/ui/src/frontend/ftrace_panel.ts b/ui/src/frontend/ftrace_panel.ts
index 9939633..84e895f 100644
--- a/ui/src/frontend/ftrace_panel.ts
+++ b/ui/src/frontend/ftrace_panel.ts
@@ -14,10 +14,10 @@
 
 import m from 'mithril';
 
+import {time, Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {colorForString} from '../common/colorizer';
 import {StringListPatch} from '../common/state';
-import {time, Time} from '../common/time';
 
 import {globals} from './globals';
 import {Panel} from './panel';
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index b18a383..17d8875 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -14,6 +14,13 @@
 
 import {BigintMath} from '../base/bigint_math';
 import {assertExists} from '../base/logging';
+import {
+  duration,
+  Span,
+  Time,
+  time,
+  TimeSpan,
+} from '../base/time';
 import {Actions, DeferredAction} from '../common/actions';
 import {AggregateData} from '../common/aggregation_data';
 import {Args} from '../common/arg_types';
@@ -37,15 +44,7 @@
   RESOLUTION_DEFAULT,
   State,
 } from '../common/state';
-import {
-  duration,
-  Span,
-  Time,
-  time,
-  TimeSpan,
-  TimestampFormat,
-  timestampFormat,
-} from '../common/time';
+import {TimestampFormat, timestampFormat} from '../common/timestamp_format';
 import {setPerfHooks} from '../core/perf';
 import {raf} from '../core/raf_scheduler';
 
diff --git a/ui/src/frontend/gridline_helper.ts b/ui/src/frontend/gridline_helper.ts
index b59e6af..04c24b3 100644
--- a/ui/src/frontend/gridline_helper.ts
+++ b/ui/src/frontend/gridline_helper.ts
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 import {assertTrue} from '../base/logging';
-import {duration, Span, time, Time} from '../common/time';
+import {duration, Span, time, Time} from '../base/time';
 
 import {TRACK_BORDER_COLOR, TRACK_SHELL_WIDTH} from './css_constants';
 import {globals} from './globals';
diff --git a/ui/src/frontend/gridline_helper_unittest.ts b/ui/src/frontend/gridline_helper_unittest.ts
index 109d643..989595f 100644
--- a/ui/src/frontend/gridline_helper_unittest.ts
+++ b/ui/src/frontend/gridline_helper_unittest.ts
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {Time, TimeSpan} from '../common/time';
+import {Time, TimeSpan} from '../base/time';
 
 import {getPattern, TickGenerator, TickType} from './gridline_helper';
 
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index 527345e..08295e9 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -35,6 +35,7 @@
   isGetCategoriesResponse,
 } from '../controller/chrome_proxy_record_controller';
 import {raf} from '../core/raf_scheduler';
+import {setScheduleFullRedraw} from '../widgets/raf';
 
 import {App} from './app';
 import {initCssConstants} from './css_constants';
@@ -177,6 +178,9 @@
 }
 
 function main() {
+  // Wire up raf for widgets.
+  setScheduleFullRedraw(() => raf.scheduleFullRedraw());
+
   setupContentSecurityPolicy();
 
   // Load the css. The load is asynchronous and the CSS is not ready by the time
diff --git a/ui/src/frontend/logs_panel.ts b/ui/src/frontend/logs_panel.ts
index 6c895b3..d1c981c 100644
--- a/ui/src/frontend/logs_panel.ts
+++ b/ui/src/frontend/logs_panel.ts
@@ -14,6 +14,7 @@
 
 import m from 'mithril';
 
+import {time, Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {HighPrecisionTimeSpan} from '../common/high_precision_time';
 import {
@@ -22,7 +23,6 @@
   LogEntries,
   LogEntriesKey,
 } from '../common/logs';
-import {time, Time} from '../common/time';
 import {raf} from '../core/raf_scheduler';
 
 import {SELECTED_LOG_ROWS_COLOR} from './css_constants';
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index ab22447..362a1ae 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -14,10 +14,10 @@
 
 import m from 'mithril';
 
+import {Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {randomColor} from '../common/colorizer';
 import {AreaNote, Note} from '../common/state';
-import {Time} from '../common/time';
 import {raf} from '../core/raf_scheduler';
 
 import {
diff --git a/ui/src/frontend/overview_timeline_panel.ts b/ui/src/frontend/overview_timeline_panel.ts
index 08edf02..852d694 100644
--- a/ui/src/frontend/overview_timeline_panel.ts
+++ b/ui/src/frontend/overview_timeline_panel.ts
@@ -14,15 +14,14 @@
 
 import m from 'mithril';
 
-import {hueForCpu} from '../common/colorizer';
 import {
   duration,
   Span,
   Time,
   time,
-  TimestampFormat,
-  timestampFormat,
-} from '../common/time';
+} from '../base/time';
+import {hueForCpu} from '../common/colorizer';
+import {timestampFormat, TimestampFormat} from '../common/timestamp_format';
 
 import {
   OVERVIEW_TIMELINE_NON_VISIBLE_COLOR,
diff --git a/ui/src/frontend/query_table.ts b/ui/src/frontend/query_table.ts
index b5d33dd..7849b49 100644
--- a/ui/src/frontend/query_table.ts
+++ b/ui/src/frontend/query_table.ts
@@ -16,10 +16,10 @@
 import m from 'mithril';
 
 import {BigintMath} from '../base/bigint_math';
+import {Duration, Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {QueryResponse} from '../common/queries';
 import {Row} from '../common/query_result';
-import {Duration, Time} from '../common/time';
 
 import {Anchor} from './anchor';
 import {copyToClipboard, queryResponseToClipboard} from './clipboard';
diff --git a/ui/src/frontend/scroll_helper.ts b/ui/src/frontend/scroll_helper.ts
index 6c3217c..38c5eb4 100644
--- a/ui/src/frontend/scroll_helper.ts
+++ b/ui/src/frontend/scroll_helper.ts
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {time} from '../base/time';
 import {Actions} from '../common/actions';
 import {
   HighPrecisionTime,
   HighPrecisionTimeSpan,
 } from '../common/high_precision_time';
 import {getContainingTrackId} from '../common/state';
-import {time} from '../common/time';
 
 import {globals} from './globals';
 
diff --git a/ui/src/frontend/slice.ts b/ui/src/frontend/slice.ts
index c8df8ba..4269187 100644
--- a/ui/src/frontend/slice.ts
+++ b/ui/src/frontend/slice.ts
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {duration, time} from '../base/time';
 import {Color} from '../common/colorizer';
-import {duration, time} from '../common/time';
 
 export interface Slice {
   // These properties are updated only once per query result when the Slice
diff --git a/ui/src/frontend/slice_details.ts b/ui/src/frontend/slice_details.ts
index 0c5993a..16075cf 100644
--- a/ui/src/frontend/slice_details.ts
+++ b/ui/src/frontend/slice_details.ts
@@ -16,8 +16,8 @@
 
 import {BigintMath} from '../base/bigint_math';
 import {sqliteString} from '../base/string_utils';
+import {Duration, duration, time} from '../base/time';
 import {exists} from '../base/utils';
-import {Duration, duration, time} from '../common/time';
 
 import {Anchor} from './anchor';
 import {addTab} from './bottom_tab';
diff --git a/ui/src/frontend/slice_panel.ts b/ui/src/frontend/slice_panel.ts
index 3582a0d..14601b1 100644
--- a/ui/src/frontend/slice_panel.ts
+++ b/ui/src/frontend/slice_panel.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {duration, time} from '../common/time';
+import {duration, time} from '../base/time';
 
 import {globals, SliceDetails} from './globals';
 import {Panel} from './panel';
diff --git a/ui/src/frontend/sql/slice.ts b/ui/src/frontend/sql/slice.ts
index d6252b1..d096723 100644
--- a/ui/src/frontend/sql/slice.ts
+++ b/ui/src/frontend/sql/slice.ts
@@ -15,6 +15,7 @@
 import m from 'mithril';
 
 import {BigintMath} from '../../base/bigint_math';
+import {duration, Time, time} from '../../base/time';
 import {exists} from '../../base/utils';
 import {Actions} from '../../common/actions';
 import {EngineProxy} from '../../common/engine';
@@ -25,7 +26,6 @@
   STR,
   STR_NULL,
 } from '../../common/query_result';
-import {duration, Time, time} from '../../common/time';
 import {Anchor} from '../anchor';
 import {globals} from '../globals';
 import {focusHorizontalRange, verticalScrollToTrack} from '../scroll_helper';
diff --git a/ui/src/frontend/sql_table/render_cell.ts b/ui/src/frontend/sql_table/render_cell.ts
index 75cdc66..19c0b28 100644
--- a/ui/src/frontend/sql_table/render_cell.ts
+++ b/ui/src/frontend/sql_table/render_cell.ts
@@ -15,8 +15,8 @@
 import m from 'mithril';
 
 import {sqliteString} from '../../base/string_utils';
+import {duration, Duration, Time} from '../../base/time';
 import {Row, SqlValue} from '../../common/query_result';
-import {duration, Duration, Time} from '../../common/time';
 import {Anchor} from '../anchor';
 import {copyToClipboard} from '../clipboard';
 import {Icons} from '../semantic_icons';
diff --git a/ui/src/frontend/sql_types.ts b/ui/src/frontend/sql_types.ts
index e769611..d34c7ee 100644
--- a/ui/src/frontend/sql_types.ts
+++ b/ui/src/frontend/sql_types.ts
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {Brand} from './brand';
+import {Brand} from '../base/brand';
 
 // Type-safe aliases for various flavours of ints Trace Processor exposes
 // (e.g. timestamp or ids into a given SQL table) and functions to work with
diff --git a/ui/src/frontend/thread_state.ts b/ui/src/frontend/thread_state.ts
index eb96432..d2b6ff5 100644
--- a/ui/src/frontend/thread_state.ts
+++ b/ui/src/frontend/thread_state.ts
@@ -14,15 +14,15 @@
 
 import m from 'mithril';
 
-import {Actions} from '../common/actions';
-import {EngineProxy} from '../common/engine';
-import {LONG, NUM, NUM_NULL, STR_NULL} from '../common/query_result';
-import {translateState} from '../common/thread_state';
 import {
   duration,
   Time,
   time,
-} from '../common/time';
+} from '../base/time';
+import {Actions} from '../common/actions';
+import {EngineProxy} from '../common/engine';
+import {LONG, NUM, NUM_NULL, STR_NULL} from '../common/query_result';
+import {translateState} from '../common/thread_state';
 
 import {Anchor} from './anchor';
 import {globals} from './globals';
diff --git a/ui/src/frontend/thread_state_tab.ts b/ui/src/frontend/thread_state_tab.ts
index c68530e..6745a46 100644
--- a/ui/src/frontend/thread_state_tab.ts
+++ b/ui/src/frontend/thread_state_tab.ts
@@ -14,8 +14,8 @@
 
 import m from 'mithril';
 
+import {Duration, time} from '../base/time';
 import {runQuery} from '../common/queries';
-import {Duration, time} from '../common/time';
 import {raf} from '../core/raf_scheduler';
 import {addDebugTrack} from '../tracks/debug/slice_track';
 
@@ -252,7 +252,7 @@
       'state',
       'blocked_function',
       'height',
-      'table_name'
+      'table_name',
     ];
 
     const sliceColumnSliceNames = [
diff --git a/ui/src/frontend/tickmark_panel.ts b/ui/src/frontend/tickmark_panel.ts
index 51bfc6e..9390c7f 100644
--- a/ui/src/frontend/tickmark_panel.ts
+++ b/ui/src/frontend/tickmark_panel.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {Time} from '../common/time';
+import {Time} from '../base/time';
 
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {globals} from './globals';
diff --git a/ui/src/frontend/time_axis_panel.ts b/ui/src/frontend/time_axis_panel.ts
index e5ca40e..e76f837 100644
--- a/ui/src/frontend/time_axis_panel.ts
+++ b/ui/src/frontend/time_axis_panel.ts
@@ -17,9 +17,8 @@
 import {
   Time,
   time,
-  TimestampFormat,
-  timestampFormat,
-} from '../common/time';
+} from '../base/time';
+import {TimestampFormat, timestampFormat} from '../common/timestamp_format';
 
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {globals} from './globals';
diff --git a/ui/src/frontend/time_scale.ts b/ui/src/frontend/time_scale.ts
index 3981ded..8710d13 100644
--- a/ui/src/frontend/time_scale.ts
+++ b/ui/src/frontend/time_scale.ts
@@ -13,11 +13,11 @@
 // limitations under the License.
 
 import {assertTrue} from '../base/logging';
+import {duration, Span, time} from '../base/time';
 import {
   HighPrecisionTime,
   HighPrecisionTimeSpan,
 } from '../common/high_precision_time';
-import {duration, Span, time} from '../common/time';
 
 export class TimeScale {
   private _start: HighPrecisionTime;
diff --git a/ui/src/frontend/time_scale_unittest.ts b/ui/src/frontend/time_scale_unittest.ts
index e0a3eac..519cbba 100644
--- a/ui/src/frontend/time_scale_unittest.ts
+++ b/ui/src/frontend/time_scale_unittest.ts
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Time} from '../base/time';
 import {HighPrecisionTime} from '../common/high_precision_time';
-import {Time} from '../common/time';
 
 import {PxSpan, TimeScale} from './time_scale';
 
diff --git a/ui/src/frontend/time_selection_panel.ts b/ui/src/frontend/time_selection_panel.ts
index ea4dacd..4e4fdbd 100644
--- a/ui/src/frontend/time_selection_panel.ts
+++ b/ui/src/frontend/time_selection_panel.ts
@@ -21,9 +21,8 @@
   time,
   Time,
   TimeSpan,
-  TimestampFormat,
-  timestampFormat,
-} from '../common/time';
+} from '../base/time';
+import {timestampFormat, TimestampFormat} from '../common/timestamp_format';
 
 import {
   BACKGROUND_COLOR,
diff --git a/ui/src/frontend/trace_converter.ts b/ui/src/frontend/trace_converter.ts
index f1c9b25..97acbd4 100644
--- a/ui/src/frontend/trace_converter.ts
+++ b/ui/src/frontend/trace_converter.ts
@@ -12,12 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {time} from '../base/time';
 import {Actions} from '../common/actions';
 import {
   ConversionJobName,
   ConversionJobStatus,
 } from '../common/conversion_jobs';
-import {time} from '../common/time';
 
 import {download} from './clipboard';
 import {maybeShowErrorDialog} from './error_dialog';
diff --git a/ui/src/frontend/track.ts b/ui/src/frontend/track.ts
index 2f4e87b..b39ac7c 100644
--- a/ui/src/frontend/track.ts
+++ b/ui/src/frontend/track.ts
@@ -15,9 +15,9 @@
 import m from 'mithril';
 
 import {assertExists} from '../base/logging';
+import {duration, Span, time} from '../base/time';
 import {EngineProxy} from '../common/engine';
 import {TrackState} from '../common/state';
-import {duration, Span, time} from '../common/time';
 import {TrackData} from '../common/track_data';
 import {TrackLike} from '../public';
 
diff --git a/ui/src/frontend/track_cache.ts b/ui/src/frontend/track_cache.ts
index f253711..63d0939 100644
--- a/ui/src/frontend/track_cache.ts
+++ b/ui/src/frontend/track_cache.ts
@@ -14,7 +14,7 @@
 
 import {BigintMath} from '../base/bigint_math';
 import {assertTrue} from '../base/logging';
-import {duration, time, Time} from '../common/time';
+import {duration, time, Time} from '../base/time';
 
 export const BUCKETS_PER_PIXEL = 2;
 
diff --git a/ui/src/frontend/track_cache_unittest.ts b/ui/src/frontend/track_cache_unittest.ts
index 26e4e30..b3a273e 100644
--- a/ui/src/frontend/track_cache_unittest.ts
+++ b/ui/src/frontend/track_cache_unittest.ts
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {Time} from '../common/time';
+import {Time} from '../base/time';
 
 import {CacheKey, TrackCache} from './track_cache';
 
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 3263965..0bef093 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -15,11 +15,11 @@
 import {hex} from 'color-convert';
 import m from 'mithril';
 
+import {duration, Span, time} from '../base/time';
 import {Actions} from '../common/actions';
 import {pluginManager} from '../common/plugins';
 import {RegistryError} from '../common/registry';
 import {TrackState} from '../common/state';
-import {duration, Span, time} from '../common/time';
 import {raf} from '../core/raf_scheduler';
 import {TrackLike} from '../public';
 
diff --git a/ui/src/frontend/vertical_line_helper.ts b/ui/src/frontend/vertical_line_helper.ts
index 1a0c47f..1dc926b 100644
--- a/ui/src/frontend/vertical_line_helper.ts
+++ b/ui/src/frontend/vertical_line_helper.ts
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {time} from '../common/time';
+import {time} from '../base/time';
 
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {TimeScale} from './time_scale';
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 68c6d7e..2f3a2c6 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -15,9 +15,9 @@
 import m from 'mithril';
 
 import {clamp} from '../base/math_utils';
+import {Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {featureFlags} from '../common/feature_flags';
-import {Time} from '../common/time';
 import {raf} from '../core/raf_scheduler';
 
 import {TRACK_SHELL_WIDTH} from './css_constants';
diff --git a/ui/src/frontend/widgets/duration.ts b/ui/src/frontend/widgets/duration.ts
index 1b85fbb..b8a16eb 100644
--- a/ui/src/frontend/widgets/duration.ts
+++ b/ui/src/frontend/widgets/duration.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {Duration, duration} from '../../common/time';
+import {Duration, duration} from '../../base/time';
 
 interface DurationWidgetAttrs {
   dur: duration;
diff --git a/ui/src/frontend/widgets/multiselect.ts b/ui/src/frontend/widgets/multiselect.ts
index 3ef0bc7..c537868 100644
--- a/ui/src/frontend/widgets/multiselect.ts
+++ b/ui/src/frontend/widgets/multiselect.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {raf} from '../../core/raf_scheduler';
+import {scheduleFullRedraw} from '../../widgets/raf';
 import {DESELECT, SELECT_ALL} from '../icons';
 
 import {Button} from './button';
@@ -114,7 +114,7 @@
                               options.filter(({checked}) => checked)
                                   .map(({id}) => ({id, checked: false}));
                           onChange(diffs);
-                          raf.scheduleFullRedraw();
+                          scheduleFullRedraw();
                         },
                         disabled: !anyChecked,
                       }),
@@ -138,7 +138,7 @@
                       const diffs = options.filter(({checked}) => !checked)
                                         .map(({id}) => ({id, checked: true}));
                       onChange(diffs);
-                      raf.scheduleFullRedraw();
+                      scheduleFullRedraw();
                     },
                     disabled: allChecked,
                   }),
@@ -152,7 +152,7 @@
                       const diffs = options.filter(({checked}) => checked)
                                         .map(({id}) => ({id, checked: false}));
                       onChange(diffs);
-                      raf.scheduleFullRedraw();
+                      scheduleFullRedraw();
                     },
                     disabled: !anyChecked,
                   }),
@@ -170,7 +170,7 @@
           oninput: (event: Event) => {
             const eventTarget = event.target as HTMLTextAreaElement;
             this.searchText = eventTarget.value;
-            raf.scheduleFullRedraw();
+            scheduleFullRedraw();
           },
           value: this.searchText,
           placeholder: 'Filter options...',
@@ -185,7 +185,7 @@
       return m(Button, {
         onclick: () => {
           this.searchText = '';
-          raf.scheduleFullRedraw();
+          scheduleFullRedraw();
         },
         label: '',
         icon: 'close',
@@ -210,7 +210,7 @@
         classes: 'pf-multiselect-item',
         onchange: () => {
           onChange([{id, checked: !checked}]);
-          raf.scheduleFullRedraw();
+          scheduleFullRedraw();
         },
       });
     });
diff --git a/ui/src/frontend/widgets/popup.ts b/ui/src/frontend/widgets/popup.ts
index 8fa603c..ba19fa6 100644
--- a/ui/src/frontend/widgets/popup.ts
+++ b/ui/src/frontend/widgets/popup.ts
@@ -19,7 +19,7 @@
 import {classNames} from '../classnames';
 import {findRef, isOrContains, toHTMLElement} from '../../base/dom_utils';
 import {assertExists} from '../../base/logging';
-import {raf} from '../../core/raf_scheduler';
+import {scheduleFullRedraw} from '../../widgets/raf';
 
 type CustomModifier = Modifier<'sameWidth', {}>;
 type ExtendedModifiers = StrictModifiers|CustomModifier;
@@ -336,13 +336,13 @@
     if (this.isOpen) {
       this.isOpen = false;
       this.onChange(this.isOpen);
-      raf.scheduleFullRedraw();
+      scheduleFullRedraw();
     }
   }
 
   private togglePopup() {
     this.isOpen = !this.isOpen;
     this.onChange(this.isOpen);
-    raf.scheduleFullRedraw();
+    scheduleFullRedraw();
   }
 }
diff --git a/ui/src/frontend/widgets/select.ts b/ui/src/frontend/widgets/select.ts
index 7224509..abae62d 100644
--- a/ui/src/frontend/widgets/select.ts
+++ b/ui/src/frontend/widgets/select.ts
@@ -15,7 +15,7 @@
 import m from 'mithril';
 
 import {exists} from '../../base/utils';
-import {raf} from '../../core/raf_scheduler';
+import {scheduleFullRedraw} from '../../widgets/raf';
 
 import {Menu, MenuItem} from './menu';
 import {TextInput} from './text_input';
@@ -77,7 +77,7 @@
             oninput: (event: Event) => {
               const eventTarget = event.target as HTMLTextAreaElement;
               this.searchText = eventTarget.value;
-              raf.scheduleFullRedraw();
+              scheduleFullRedraw();
             },
             onload: (event: Event) => {
               if (!attrs.autofocusInput) return;
diff --git a/ui/src/frontend/widgets/timestamp.ts b/ui/src/frontend/widgets/timestamp.ts
index 441c222..aeadaa4 100644
--- a/ui/src/frontend/widgets/timestamp.ts
+++ b/ui/src/frontend/widgets/timestamp.ts
@@ -14,13 +14,9 @@
 
 import m from 'mithril';
 
+import {time, Time} from '../../base/time';
 import {Actions} from '../../common/actions';
-import {
-  time,
-  Time,
-  TimestampFormat,
-  timestampFormat,
-} from '../../common/time';
+import {TimestampFormat, timestampFormat} from '../../common/timestamp_format';
 import {Anchor} from '../anchor';
 import {copyToClipboard} from '../clipboard';
 import {globals} from '../globals';
diff --git a/ui/src/frontend/widgets/tree.ts b/ui/src/frontend/widgets/tree.ts
index 9e7462b..285a772 100644
--- a/ui/src/frontend/widgets/tree.ts
+++ b/ui/src/frontend/widgets/tree.ts
@@ -15,7 +15,7 @@
 import m from 'mithril';
 
 import {hasChildren} from '../../base/mithril_utils';
-import {raf} from '../../core/raf_scheduler';
+import {scheduleFullRedraw} from '../../widgets/raf';
 import {classNames} from '../classnames';
 
 // Heirachical tree layout but right values are horizontally aligned.
@@ -81,7 +81,7 @@
           onclick: () => {
             this.collapsed = !this.isCollapsed(vnode);
             onCollapseChanged(this.collapsed, attrs);
-            raf.scheduleFullRedraw();
+            scheduleFullRedraw();
           },
         }),
         m(
@@ -210,19 +210,19 @@
               // Expanding
               if (this.renderChildren) {
                 this.collapsed = false;
-                raf.scheduleFullRedraw();
+                scheduleFullRedraw();
               } else {
                 this.loading = true;
                 fetchData().then((result) => {
                   this.loading = false;
                   this.collapsed = false;
                   this.renderChildren = result;
-                  raf.scheduleFullRedraw();
+                  scheduleFullRedraw();
                 });
               }
             }
             this.collapsed = collapsed;
-            raf.scheduleFullRedraw();
+            scheduleFullRedraw();
           },
         },
         this.renderChildren && this.renderChildren());
diff --git a/ui/src/frontend/widgets/vega_view.ts b/ui/src/frontend/widgets/vega_view.ts
index 51a78c1..fb6b3d1 100644
--- a/ui/src/frontend/widgets/vega_view.ts
+++ b/ui/src/frontend/widgets/vega_view.ts
@@ -22,7 +22,7 @@
 import {EngineProxy} from '../../common/engine';
 import {getErrorMessage} from '../../common/errors';
 import {QueryError} from '../../common/query_result';
-import {raf} from '../../core/raf_scheduler';
+import {scheduleFullRedraw} from '../../widgets/raf';
 
 import {Spinner} from './spinner';
 
@@ -227,7 +227,7 @@
     }
     this._status = Status.Done;
     this.pending = undefined;
-    raf.scheduleFullRedraw();
+    scheduleFullRedraw();
   }
 
   private handleError(pending: Promise<vega.View>, err: unknown) {
@@ -241,7 +241,7 @@
   private setError(err: unknown) {
     this._status = Status.Error;
     this._error = getErrorMessage(err);
-    raf.scheduleFullRedraw();
+    scheduleFullRedraw();
   }
 
   dispose() {
diff --git a/ui/src/public/index.ts b/ui/src/public/index.ts
index ffe4fc6..ba205d0 100644
--- a/ui/src/public/index.ts
+++ b/ui/src/public/index.ts
@@ -15,8 +15,8 @@
 import m from 'mithril';
 
 import {Hotkey} from '../base/hotkeys';
+import {duration, Span, time} from '../base/time';
 import {EngineProxy} from '../common/engine';
-import {duration, Span, time} from '../common/time';
 import {TrackControllerFactory} from '../controller/track_controller';
 import {Store} from '../frontend/store';
 import {PxSpan, TimeScale} from '../frontend/time_scale';
diff --git a/ui/src/traceconv/index.ts b/ui/src/traceconv/index.ts
index 5c504a1..627df91 100644
--- a/ui/src/traceconv/index.ts
+++ b/ui/src/traceconv/index.ts
@@ -14,11 +14,11 @@
 
 import {defer} from '../base/deferred';
 import {assertExists, reportError, setErrorHandler} from '../base/logging';
+import {time} from '../base/time';
 import {
   ConversionJobName,
   ConversionJobStatus,
 } from '../common/conversion_jobs';
-import {time} from '../common/time';
 import traceconv from '../gen/traceconv';
 
 const selfWorker = self as {} as Worker;
diff --git a/ui/src/tracks/actual_frames/index.ts b/ui/src/tracks/actual_frames/index.ts
index 0d48902..5e1d631 100644
--- a/ui/src/tracks/actual_frames/index.ts
+++ b/ui/src/tracks/actual_frames/index.ts
@@ -13,8 +13,8 @@
 // limitations under the License.
 
 import {BigintMath as BIMath} from '../../base/bigint_math';
+import {duration, time} from '../../base/time';
 import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
-import {duration, time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {TrackController} from '../../controller/track_controller';
 import {NewTrackArgs, Track} from '../../frontend/track';
diff --git a/ui/src/tracks/android_log/index.ts b/ui/src/tracks/android_log/index.ts
index 5d4754a..b777808 100644
--- a/ui/src/tracks/android_log/index.ts
+++ b/ui/src/tracks/android_log/index.ts
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {duration, Time, time} from '../../base/time';
 import {LONG, NUM} from '../../common/query_result';
-import {duration, Time, time} from '../../common/time';
 import {LIMIT, TrackData} from '../../common/track_data';
 import {
   TrackController,
diff --git a/ui/src/tracks/async_slices/index.ts b/ui/src/tracks/async_slices/index.ts
index 25268d5..d25717f 100644
--- a/ui/src/tracks/async_slices/index.ts
+++ b/ui/src/tracks/async_slices/index.ts
@@ -13,8 +13,8 @@
 // limitations under the License.
 
 import {BigintMath as BIMath} from '../../base/bigint_math';
+import {duration, time} from '../../base/time';
 import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
-import {duration, time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {
   TrackController,
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts b/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
index b97c46f..5f0d377 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
@@ -14,9 +14,9 @@
 
 import m from 'mithril';
 
+import {duration, Time, time} from '../../base/time';
 import {exists} from '../../base/utils';
 import {LONG, NUM, STR} from '../../common/query_result';
-import {duration, Time, time} from '../../common/time';
 import {raf} from '../../core/raf_scheduler';
 import {
   BottomTab,
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_slice.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_slice.ts
index 9b0d155..e4ea041 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_slice.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_slice.ts
@@ -14,10 +14,10 @@
 
 import m from 'mithril';
 
+import {duration, time, Time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {EngineProxy} from '../../common/engine';
 import {LONG, NUM} from '../../common/query_result';
-import {duration, time, Time} from '../../common/time';
 import {Anchor} from '../../frontend/anchor';
 import {globals} from '../../frontend/globals';
 import {scrollToTrackAndTs} from '../../frontend/scroll_helper';
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_details_panel.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
index ee2b554..5547b38 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
@@ -14,10 +14,10 @@
 
 import m from 'mithril';
 
+import {duration, Time, time} from '../../base/time';
 import {exists} from '../../base/utils';
 import {EngineProxy} from '../../common/engine';
 import {LONG, NUM, STR} from '../../common/query_result';
-import {duration, Time, time} from '../../common/time';
 import {raf} from '../../core/raf_scheduler';
 import {
   BottomTab,
diff --git a/ui/src/tracks/chrome_slices/index.ts b/ui/src/tracks/chrome_slices/index.ts
index 5a0c82b..875a7bf 100644
--- a/ui/src/tracks/chrome_slices/index.ts
+++ b/ui/src/tracks/chrome_slices/index.ts
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 import {BigintMath as BIMath} from '../../base/bigint_math';
+import {duration, Span, Time, time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {cropText, drawIncompleteSlice} from '../../common/canvas_utils';
 import {
@@ -21,7 +22,6 @@
 } from '../../common/colorizer';
 import {HighPrecisionTime} from '../../common/high_precision_time';
 import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
-import {duration, Span, Time, time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {TrackController} from '../../controller/track_controller';
 import {checkerboardExcept} from '../../frontend/checkerboard';
diff --git a/ui/src/tracks/counter/index.ts b/ui/src/tracks/counter/index.ts
index 3124740..7f8c20b 100644
--- a/ui/src/tracks/counter/index.ts
+++ b/ui/src/tracks/counter/index.ts
@@ -16,8 +16,8 @@
 
 import {searchSegment} from '../../base/binary_search';
 import {assertTrue} from '../../base/logging';
+import {duration, time, Time} from '../../base/time';
 import {Actions} from '../../common/actions';
-import {duration, time, Time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {TrackController} from '../../controller/track_controller';
 import {checkerboardExcept} from '../../frontend/checkerboard';
diff --git a/ui/src/tracks/cpu_freq/index.ts b/ui/src/tracks/cpu_freq/index.ts
index 40d221e..55eac81 100644
--- a/ui/src/tracks/cpu_freq/index.ts
+++ b/ui/src/tracks/cpu_freq/index.ts
@@ -15,6 +15,7 @@
 import {BigintMath as BIMath} from '../../base/bigint_math';
 import {searchSegment} from '../../base/binary_search';
 import {assertTrue} from '../../base/logging';
+import {duration, time, Time} from '../../base/time';
 import {hueForCpu} from '../../common/colorizer';
 import {
   LONG,
@@ -23,7 +24,6 @@
   NUM_NULL,
   QueryResult,
 } from '../../common/query_result';
-import {duration, time, Time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {TrackController} from '../../controller/track_controller';
 import {checkerboardExcept} from '../../frontend/checkerboard';
diff --git a/ui/src/tracks/cpu_profile/index.ts b/ui/src/tracks/cpu_profile/index.ts
index f57ec35..7045ac0 100644
--- a/ui/src/tracks/cpu_profile/index.ts
+++ b/ui/src/tracks/cpu_profile/index.ts
@@ -14,10 +14,10 @@
 
 
 import {searchSegment} from '../../base/binary_search';
+import {duration, Time, time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {hslForSlice} from '../../common/colorizer';
 import {LONG, NUM} from '../../common/query_result';
-import {duration, Time, time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {
   TrackController,
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index 602aa3c..2601003 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -15,6 +15,7 @@
 import {BigintMath as BIMath} from '../../base/bigint_math';
 import {search, searchEq, searchSegment} from '../../base/binary_search';
 import {assertTrue} from '../../base/logging';
+import {Duration, duration, Time, time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {
   cropText,
@@ -23,7 +24,6 @@
 } from '../../common/canvas_utils';
 import {colorForThread} from '../../common/colorizer';
 import {LONG, NUM} from '../../common/query_result';
-import {Duration, duration, Time, time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {
   TrackController,
diff --git a/ui/src/tracks/debug/details_tab.ts b/ui/src/tracks/debug/details_tab.ts
index 1e850ac..ab4d61d 100644
--- a/ui/src/tracks/debug/details_tab.ts
+++ b/ui/src/tracks/debug/details_tab.ts
@@ -16,8 +16,14 @@
 
 import {GridLayout} from '../..//frontend/widgets/grid_layout';
 import {Section} from '../..//frontend/widgets/section';
-import {ColumnType, LONG, STR} from '../../common/query_result';
-import {Duration, duration, Time, time} from '../../common/time';
+import {duration, Time, time} from '../../base/time';
+import {
+  ColumnType,
+  durationFromSql,
+  LONG,
+  STR,
+  timeFromSql,
+} from '../../common/query_result';
 import {raf} from '../../core/raf_scheduler';
 import {
   BottomTab,
@@ -216,8 +222,8 @@
     }
     const details = dictToTreeNodes({
       'Name': this.data['name'] as string,
-      'Start time': m(Timestamp, {ts: Time.fromSql(this.data['ts'])}),
-      'Duration': m(DurationWidget, {dur: Duration.fromSql(this.data['dur'])}),
+      'Start time': m(Timestamp, {ts: timeFromSql(this.data['ts'])}),
+      'Duration': m(DurationWidget, {dur: durationFromSql(this.data['dur'])}),
       'Debug slice id': `${this.config.sqlTableName}[${this.config.id}]`,
     });
     details.push(this.renderThreadStateInfo());
diff --git a/ui/src/tracks/expected_frames/index.ts b/ui/src/tracks/expected_frames/index.ts
index 17fe346..dce354c 100644
--- a/ui/src/tracks/expected_frames/index.ts
+++ b/ui/src/tracks/expected_frames/index.ts
@@ -20,7 +20,7 @@
 import {ChromeSliceTrack} from '../chrome_slices';
 
 import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
-import {duration, time} from '../../common/time';
+import {duration, time} from '../../base/time';
 import {
   TrackController,
 } from '../../controller/track_controller';
diff --git a/ui/src/tracks/ftrace/index.ts b/ui/src/tracks/ftrace/index.ts
index a6cccae..5a576a5 100644
--- a/ui/src/tracks/ftrace/index.ts
+++ b/ui/src/tracks/ftrace/index.ts
@@ -14,9 +14,9 @@
 
 import {Vnode} from 'mithril';
 
+import {duration, Time, time} from '../../base/time';
 import {colorForString} from '../../common/colorizer';
 import {LONG, NUM, STR} from '../../common/query_result';
-import {duration, Time, time} from '../../common/time';
 import {
   TrackAdapter,
   TrackControllerAdapter,
diff --git a/ui/src/tracks/heap_profile/index.ts b/ui/src/tracks/heap_profile/index.ts
index 8bd761d..08018ce 100644
--- a/ui/src/tracks/heap_profile/index.ts
+++ b/ui/src/tracks/heap_profile/index.ts
@@ -13,10 +13,10 @@
 // limitations under the License.
 
 import {searchSegment} from '../../base/binary_search';
+import {duration, Time, time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {LONG, STR} from '../../common/query_result';
 import {ProfileType} from '../../common/state';
-import {duration, Time, time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {profileType} from '../../controller/flamegraph_controller';
 import {TrackController} from '../../controller/track_controller';
diff --git a/ui/src/tracks/perf_samples_profile/index.ts b/ui/src/tracks/perf_samples_profile/index.ts
index 120941e..ff836ad 100644
--- a/ui/src/tracks/perf_samples_profile/index.ts
+++ b/ui/src/tracks/perf_samples_profile/index.ts
@@ -13,10 +13,10 @@
 // limitations under the License.
 
 import {searchSegment} from '../../base/binary_search';
+import {duration, Time, time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {LONG} from '../../common/query_result';
 import {ProfileType} from '../../common/state';
-import {duration, Time, time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {TrackController} from '../../controller/track_controller';
 import {FLAMEGRAPH_HOVERED_COLOR} from '../../frontend/flamegraph';
diff --git a/ui/src/tracks/process_scheduling/index.ts b/ui/src/tracks/process_scheduling/index.ts
index 4d600ce..69e580f 100644
--- a/ui/src/tracks/process_scheduling/index.ts
+++ b/ui/src/tracks/process_scheduling/index.ts
@@ -15,10 +15,10 @@
 import {BigintMath as BIMath} from '../../base/bigint_math';
 import {searchEq, searchRange} from '../../base/binary_search';
 import {assertTrue} from '../../base/logging';
+import {duration, time, Time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {colorForThread} from '../../common/colorizer';
 import {LONG, NUM, QueryResult} from '../../common/query_result';
-import {duration, time, Time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {TrackController} from '../../controller/track_controller';
 import {checkerboardExcept} from '../../frontend/checkerboard';
diff --git a/ui/src/tracks/process_summary/index.ts b/ui/src/tracks/process_summary/index.ts
index 5d1377c..ea795a4 100644
--- a/ui/src/tracks/process_summary/index.ts
+++ b/ui/src/tracks/process_summary/index.ts
@@ -14,9 +14,9 @@
 
 import {BigintMath} from '../../base/bigint_math';
 import {assertFalse} from '../../base/logging';
+import {duration, Time, time} from '../../base/time';
 import {colorForTid} from '../../common/colorizer';
 import {NUM} from '../../common/query_result';
-import {duration, Time, time} from '../../common/time';
 import {LIMIT, TrackData} from '../../common/track_data';
 import {TrackController} from '../../controller/track_controller';
 import {checkerboardExcept} from '../../frontend/checkerboard';
diff --git a/ui/src/tracks/thread_state/index.ts b/ui/src/tracks/thread_state/index.ts
index 1aeb5c5..b684e6c 100644
--- a/ui/src/tracks/thread_state/index.ts
+++ b/ui/src/tracks/thread_state/index.ts
@@ -15,12 +15,12 @@
 import {BigintMath as BIMath} from '../../base/bigint_math';
 import {search} from '../../base/binary_search';
 import {assertFalse} from '../../base/logging';
+import {duration, Time, time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {cropText} from '../../common/canvas_utils';
 import {colorForState} from '../../common/colorizer';
 import {LONG, NUM, NUM_NULL, STR_NULL} from '../../common/query_result';
 import {translateState} from '../../common/thread_state';
-import {duration, Time, time} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {TrackController} from '../../controller/track_controller';
 import {checkerboardExcept} from '../../frontend/checkerboard';
diff --git a/ui/src/widgets/raf.ts b/ui/src/widgets/raf.ts
new file mode 100644
index 0000000..20afb61
--- /dev/null
+++ b/ui/src/widgets/raf.ts
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+let FULL_REDRAW_FUNCTION = () => {};
+
+export function setScheduleFullRedraw(func: () => void) {
+  FULL_REDRAW_FUNCTION = func;
+}
+
+export function scheduleFullRedraw() {
+  FULL_REDRAW_FUNCTION();
+}