// 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. import m from 'mithril'; import {LegacySelection} from '../common/state'; import {BottomTab} from '../frontend/bottom_tab'; import {LegacyDetailsPanel, Tab} from '.'; export function getTrackName( args: Partial<{ name: string | null; utid: number | null; processName: string | null; pid: number | null; threadName: string | null; tid: number | null; upid: number | null; userName: string | null; uid: number | null; kind: string; threadTrack: boolean; uidTrack: boolean; }>, ) { const { name, upid, utid, processName, threadName, pid, tid, userName, uid, kind, threadTrack, uidTrack, } = args; const hasName = name !== undefined && name !== null && name !== '[NULL]'; const hasUpid = upid !== undefined && upid !== null; const hasUtid = utid !== undefined && utid !== null; const hasProcessName = processName !== undefined && processName !== null; const hasThreadName = threadName !== undefined && threadName !== null; const hasUserName = userName !== undefined && userName !== null; const hasTid = tid !== undefined && tid !== null; const hasPid = pid !== undefined && pid !== null; const hasUid = uid !== undefined && uid !== null; const hasKind = kind !== undefined; const isThreadTrack = threadTrack !== undefined && threadTrack; const isUidTrack = uidTrack !== undefined && uidTrack; // If we don't have any useful information (better than // upid/utid) we show the track kind to help with tracking // down where this is coming from. const kindSuffix = hasKind ? ` (${kind})` : ''; if (isThreadTrack && hasName && hasTid) { return `${name} (${tid})`; } else if (isUidTrack && hasName && hasUserName) { return `${name} (${userName})`; } else if (isUidTrack && hasName && hasUid) { return `${name} ${uid}`; } else if (hasName) { return `${name}`; } else if (hasUpid && hasPid && hasProcessName) { return `${processName} ${pid}`; } else if (hasUpid && hasPid) { return `Process ${pid}`; } else if (hasThreadName && hasTid) { return `${threadName} ${tid}`; } else if (hasTid) { return `Thread ${tid}`; } else if (hasUpid) { return `upid: ${upid}${kindSuffix}`; } else if (hasUtid) { return `utid: ${utid}${kindSuffix}`; } else if (hasUid) { return `uid: ${uid}${kindSuffix}`; } else if (hasKind) { return `Unnamed ${kind}`; } return 'Unknown'; } export interface BottomTabAdapterAttrs { tabFactory: (sel: LegacySelection) => BottomTab | undefined; } /** * This adapter wraps a BottomTab, converting it into a the new "current * selection" API. * This adapter is required because most bottom tab implementations expect to * be created when the selection changes, however current selection sections * stick around in memory forever and produce a section only when they detect a * relevant selection. * This adapter, given a bottom tab factory function, will simply call the * factory function whenever the selection changes. It's up to the implementer * to work out whether the selection is relevant and to construct a bottom tab. * * @example * new BottomTabAdapter({ tabFactory: (sel) => { if (sel.kind !== 'SLICE') { return undefined; } return new ChromeSliceDetailsTab({ config: { table: sel.table ?? 'slice', id: sel.id, }, engine: ctx.engine, uuid: uuidv4(), }); }, }) */ export class BottomTabToSCSAdapter implements LegacyDetailsPanel { private oldSelection?: LegacySelection; private bottomTab?: BottomTab; private attrs: BottomTabAdapterAttrs; constructor(attrs: BottomTabAdapterAttrs) { this.attrs = attrs; } render(selection: LegacySelection): m.Children { // Detect selection changes, assuming selection is immutable if (selection !== this.oldSelection) { this.oldSelection = selection; this.bottomTab = this.attrs.tabFactory(selection); } return this.bottomTab?.renderPanel(); } // Note: Must be called after render() isLoading(): boolean { return this.bottomTab?.isLoading() ?? false; } } /** * This adapter wraps a BottomTab, converting it to work with the Tab API. */ export class BottomTabToTabAdapter implements Tab { constructor(private bottomTab: BottomTab) {} getTitle(): string { return this.bottomTab.getTitle(); } render(): m.Children { return this.bottomTab.viewTab(); } }