• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2019 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {Actions} from '../common/actions';
16import {getContainingTrackId} from '../common/state';
17import {fromNs, TimeSpan, toNs} from '../common/time';
18
19import {globals} from './globals';
20
21const INCOMPLETE_SLICE_TIME_S = 0.00003;
22
23/**
24 * Given a timestamp, if |ts| is not currently in view move the view to
25 * center |ts|, keeping the same zoom level.
26 */
27export function horizontalScrollToTs(ts: number) {
28  const startNs = toNs(globals.frontendLocalState.visibleWindowTime.start);
29  const endNs = toNs(globals.frontendLocalState.visibleWindowTime.end);
30  const currentViewNs = endNs - startNs;
31  if (ts < startNs || ts > endNs) {
32    // TODO(hjd): This is an ugly jump, we should do a smooth pan instead.
33    globals.frontendLocalState.updateVisibleTime(new TimeSpan(
34        fromNs(ts - currentViewNs / 2), fromNs(ts + currentViewNs / 2)));
35  }
36}
37
38/**
39 * Given a start and end timestamp (in ns), move the view to center this range
40 * and zoom to a level where the range is 1/5 of the viewport.
41 */
42export function horizontalScrollAndZoomToRange(startTs: number, endTs: number) {
43  const visibleDur = globals.frontendLocalState.visibleWindowTime.end -
44      globals.frontendLocalState.visibleWindowTime.start;
45  let selectDur = endTs - startTs;
46  if (toNs(selectDur) === -1) {  // Unfinished slice
47    selectDur = INCOMPLETE_SLICE_TIME_S;
48    endTs = startTs;
49  }
50  const viewStartNs = toNs(globals.frontendLocalState.visibleWindowTime.start);
51  const viewEndNs = toNs(globals.frontendLocalState.visibleWindowTime.end);
52  if (selectDur / visibleDur < 0.05 || startTs < viewStartNs ||
53      endTs > viewEndNs) {
54    globals.frontendLocalState.updateVisibleTime(
55        new TimeSpan(startTs - (selectDur * 2), endTs + (selectDur * 2)));
56  }
57}
58
59/**
60 * Given a track id, find a track with that id and scroll it into view. If the
61 * track is nested inside a track group, scroll to that track group instead.
62 * If |openGroup| then open the track group and scroll to the track.
63 */
64export function verticalScrollToTrack(
65    trackId: string|number, openGroup = false) {
66  const trackIdString = `${trackId}`;
67  const track = document.querySelector('#track_' + trackIdString);
68
69  if (track) {
70    // block: 'nearest' means that it will only scroll if the track is not
71    // currently in view.
72    track.scrollIntoView({behavior: 'smooth', block: 'nearest'});
73    return;
74  }
75
76  let trackGroup = null;
77  const trackGroupId = getContainingTrackId(globals.state, trackIdString);
78  if (trackGroupId) {
79    trackGroup = document.querySelector('#track_' + trackGroupId);
80  }
81
82  if (!trackGroupId || !trackGroup) {
83    console.error(`Can't scroll, track (${trackIdString}) not found.`);
84    return;
85  }
86
87  // The requested track is inside a closed track group, either open the track
88  // group and scroll to the track or just scroll to the track group.
89  if (openGroup) {
90    // After the track exists in the dom, it will be scrolled to.
91    globals.frontendLocalState.scrollToTrackId = trackId;
92    globals.dispatch(Actions.toggleTrackGroupCollapsed({trackGroupId}));
93    return;
94  } else {
95    trackGroup.scrollIntoView({behavior: 'smooth', block: 'nearest'});
96  }
97}
98
99
100/**
101 * Scroll vertically and horizontally to reach track (|trackId|) at |ts|.
102 */
103export function scrollToTrackAndTs(
104    trackId: string|number|undefined, ts: number, openGroup = false) {
105  if (trackId !== undefined) {
106    verticalScrollToTrack(trackId, openGroup);
107  }
108  horizontalScrollToTs(ts);
109}
110
111/**
112 * Returns the UI track Id that is associated with the given |traceTrackId| in
113 * the trace_processor. Due to concepts like Async tracks and TrackGroups this
114 * is not always a one to one mapping.
115 */
116export function findUiTrackId(traceTrackId: number) {
117  for (const [uiTrackId, trackState] of Object.entries(globals.state.tracks)) {
118    const config = trackState.config as {trackId: number};
119    if (config.trackId === traceTrackId) return uiTrackId;
120    const multiple = trackState.config as {trackIds: number[]};
121    if (multiple.trackIds !== undefined &&
122        multiple.trackIds.includes(traceTrackId)) {
123      return uiTrackId;
124    }
125  }
126  return null;
127}
128