1// Copyright (C) 2023 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 m from 'mithril'; 16 17import {LegacySelection} from '../common/state'; 18import {BottomTab} from '../frontend/bottom_tab'; 19 20import {LegacyDetailsPanel, Tab} from '.'; 21 22export function getTrackName( 23 args: Partial<{ 24 name: string | null; 25 utid: number | null; 26 processName: string | null; 27 pid: number | null; 28 threadName: string | null; 29 tid: number | null; 30 upid: number | null; 31 userName: string | null; 32 uid: number | null; 33 kind: string; 34 threadTrack: boolean; 35 uidTrack: boolean; 36 }>, 37) { 38 const { 39 name, 40 upid, 41 utid, 42 processName, 43 threadName, 44 pid, 45 tid, 46 userName, 47 uid, 48 kind, 49 threadTrack, 50 uidTrack, 51 } = args; 52 53 const hasName = name !== undefined && name !== null && name !== '[NULL]'; 54 const hasUpid = upid !== undefined && upid !== null; 55 const hasUtid = utid !== undefined && utid !== null; 56 const hasProcessName = processName !== undefined && processName !== null; 57 const hasThreadName = threadName !== undefined && threadName !== null; 58 const hasUserName = userName !== undefined && userName !== null; 59 const hasTid = tid !== undefined && tid !== null; 60 const hasPid = pid !== undefined && pid !== null; 61 const hasUid = uid !== undefined && uid !== null; 62 const hasKind = kind !== undefined; 63 const isThreadTrack = threadTrack !== undefined && threadTrack; 64 const isUidTrack = uidTrack !== undefined && uidTrack; 65 66 // If we don't have any useful information (better than 67 // upid/utid) we show the track kind to help with tracking 68 // down where this is coming from. 69 const kindSuffix = hasKind ? ` (${kind})` : ''; 70 71 if (isThreadTrack && hasName && hasTid) { 72 return `${name} (${tid})`; 73 } else if (isUidTrack && hasName && hasUserName) { 74 return `${name} (${userName})`; 75 } else if (isUidTrack && hasName && hasUid) { 76 return `${name} ${uid}`; 77 } else if (hasName) { 78 return `${name}`; 79 } else if (hasUpid && hasPid && hasProcessName) { 80 return `${processName} ${pid}`; 81 } else if (hasUpid && hasPid) { 82 return `Process ${pid}`; 83 } else if (hasThreadName && hasTid) { 84 return `${threadName} ${tid}`; 85 } else if (hasTid) { 86 return `Thread ${tid}`; 87 } else if (hasUpid) { 88 return `upid: ${upid}${kindSuffix}`; 89 } else if (hasUtid) { 90 return `utid: ${utid}${kindSuffix}`; 91 } else if (hasUid) { 92 return `uid: ${uid}${kindSuffix}`; 93 } else if (hasKind) { 94 return `Unnamed ${kind}`; 95 } 96 return 'Unknown'; 97} 98 99export interface BottomTabAdapterAttrs { 100 tabFactory: (sel: LegacySelection) => BottomTab | undefined; 101} 102 103/** 104 * This adapter wraps a BottomTab, converting it into a the new "current 105 * selection" API. 106 * This adapter is required because most bottom tab implementations expect to 107 * be created when the selection changes, however current selection sections 108 * stick around in memory forever and produce a section only when they detect a 109 * relevant selection. 110 * This adapter, given a bottom tab factory function, will simply call the 111 * factory function whenever the selection changes. It's up to the implementer 112 * to work out whether the selection is relevant and to construct a bottom tab. 113 * 114 * @example 115 * new BottomTabAdapter({ 116 tabFactory: (sel) => { 117 if (sel.kind !== 'SLICE') { 118 return undefined; 119 } 120 return new ChromeSliceDetailsTab({ 121 config: { 122 table: sel.table ?? 'slice', 123 id: sel.id, 124 }, 125 engine: ctx.engine, 126 uuid: uuidv4(), 127 }); 128 }, 129 }) 130 */ 131export class BottomTabToSCSAdapter implements LegacyDetailsPanel { 132 private oldSelection?: LegacySelection; 133 private bottomTab?: BottomTab; 134 private attrs: BottomTabAdapterAttrs; 135 136 constructor(attrs: BottomTabAdapterAttrs) { 137 this.attrs = attrs; 138 } 139 140 render(selection: LegacySelection): m.Children { 141 // Detect selection changes, assuming selection is immutable 142 if (selection !== this.oldSelection) { 143 this.oldSelection = selection; 144 this.bottomTab = this.attrs.tabFactory(selection); 145 } 146 147 return this.bottomTab?.renderPanel(); 148 } 149 150 // Note: Must be called after render() 151 isLoading(): boolean { 152 return this.bottomTab?.isLoading() ?? false; 153 } 154} 155 156/** 157 * This adapter wraps a BottomTab, converting it to work with the Tab API. 158 */ 159export class BottomTabToTabAdapter implements Tab { 160 constructor(private bottomTab: BottomTab) {} 161 162 getTitle(): string { 163 return this.bottomTab.getTitle(); 164 } 165 166 render(): m.Children { 167 return this.bottomTab.viewTab(); 168 } 169} 170