| // Copyright (C) 2019 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. |
| |
| import {searchSegment} from '../base/binary_search'; |
| import {Actions} from '../common/actions'; |
| import {getContainingTrackId} from '../common/state'; |
| import {fromNs, TimeSpan, toNs} from '../common/time'; |
| |
| import {globals} from './globals'; |
| |
| export function executeSearch(reverse = false) { |
| const state = globals.frontendLocalState; |
| const index = state.searchIndex; |
| const startNs = toNs(globals.frontendLocalState.visibleWindowTime.start); |
| const endNs = toNs(globals.frontendLocalState.visibleWindowTime.end); |
| const currentTs = globals.currentSearchResults.tsStarts[index]; |
| |
| // If this is a new search or the currentTs is not in the viewport, |
| // select the first/last item in the viewport. |
| if (index === -1 || currentTs < startNs || currentTs > endNs) { |
| if (reverse) { |
| const [smaller,] = |
| searchSegment(globals.currentSearchResults.tsStarts, endNs); |
| globals.frontendLocalState.setSearchIndex(smaller); |
| } else { |
| const [, larger] = |
| searchSegment(globals.currentSearchResults.tsStarts, startNs); |
| globals.frontendLocalState.setSearchIndex(larger); |
| } |
| // If there is no result in the current viewport, move it. |
| const currentTs = globals.currentSearchResults.tsStarts[state.searchIndex]; |
| if (currentTs < startNs || currentTs > endNs) { |
| moveViewportToCurrentSearch(); |
| } |
| } else { |
| // If the currentTs is in the viewport, increment the index and move the |
| // viewport if necessary. |
| if (reverse) { |
| globals.frontendLocalState.setSearchIndex(Math.max(index - 1, 0)); |
| } else { |
| globals.frontendLocalState.setSearchIndex(Math.min( |
| index + 1, globals.currentSearchResults.sliceIds.length - 1)); |
| } |
| moveViewportToCurrentSearch(); |
| } |
| selectCurrentSearchResult(); |
| } |
| |
| function moveViewportToCurrentSearch() { |
| // Move viewport if our selection moves outside. |
| const startNs = toNs(globals.frontendLocalState.visibleWindowTime.start); |
| const endNs = toNs(globals.frontendLocalState.visibleWindowTime.end); |
| const currentTs = globals.currentSearchResults |
| .tsStarts[globals.frontendLocalState.searchIndex]; |
| const currentViewNs = endNs - startNs; |
| if (currentTs < startNs || currentTs > endNs) { |
| // TODO(taylori): This is an ugly jump, we should do a smooth pan instead. |
| globals.frontendLocalState.updateVisibleTime(new TimeSpan( |
| fromNs(currentTs - currentViewNs / 2), |
| fromNs(currentTs + currentViewNs / 2))); |
| } |
| |
| // Update vertical (up/down) scroll position |
| const trackId = globals.currentSearchResults |
| .trackIds[globals.frontendLocalState.searchIndex]; |
| let track = document.querySelector('#track_' + trackId); |
| |
| if (!track) { |
| const parentTrackId = getContainingTrackId(globals.state, trackId); |
| if (parentTrackId) { |
| track = document.querySelector('#track_' + parentTrackId); |
| } |
| } |
| |
| if (!track) { |
| console.error(`Can't scroll search result track not found (${trackId})`); |
| return; |
| } |
| |
| // block: 'nearest' means that it will only scroll if the track is not |
| // currently in view. |
| track.scrollIntoView({behavior: 'smooth', block: 'nearest'}); |
| } |
| |
| function selectCurrentSearchResult() { |
| const state = globals.frontendLocalState; |
| const index = state.searchIndex; |
| const refType = globals.currentSearchResults.refTypes[index]; |
| const currentId = globals.currentSearchResults.sliceIds[index]; |
| |
| if (currentId === undefined) return; |
| |
| if (refType === 'cpu') { |
| globals.dispatch(Actions.selectSlice({ |
| utid: globals.currentSearchResults.utids[index], |
| id: currentId, |
| })); |
| } |
| if (refType === 'utid') { |
| globals.dispatch(Actions.selectChromeSlice({ |
| id: currentId, |
| })); |
| } |
| } |