1// Copyright (C) 2022 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 {RecordingError} from '../recording_error_handling'; 16import { 17 OnTargetChangeCallback, 18 RecordingTargetV2, 19 TargetFactory, 20} from '../recording_interfaces_v2'; 21import { 22 EXTENSION_ID, 23 EXTENSION_NOT_INSTALLED, 24 isCrOS, 25} from '../recording_utils'; 26import {targetFactoryRegistry} from '../target_factory_registry'; 27import {ChromeTarget} from '../targets/chrome_target'; 28 29export const CHROME_TARGET_FACTORY = 'ChromeTargetFactory'; 30 31export class ChromeTargetFactory implements TargetFactory { 32 readonly kind = CHROME_TARGET_FACTORY; 33 // We only check the connection once at the beginning to: 34 // a) Avoid creating a 'Port' object every time 'getInfo' is called. 35 // b) When a new Port is created, the extension starts communicating with it 36 // and leaves aside the old Port objects, so creating a new Port would break 37 // any ongoing tracing session. 38 isExtensionInstalled: boolean = false; 39 private targets: ChromeTarget[] = []; 40 41 constructor() { 42 this.init(); 43 } 44 45 init() { 46 const testPort = chrome.runtime.connect(EXTENSION_ID); 47 this.isExtensionInstalled = !!testPort; 48 testPort.disconnect(); 49 50 if (!this.isExtensionInstalled) { 51 return; 52 } 53 this.targets.push(new ChromeTarget('Chrome', 'CHROME')); 54 if (isCrOS(navigator.userAgent)) { 55 this.targets.push(new ChromeTarget('ChromeOS', 'CHROME_OS')); 56 } 57 } 58 59 connectNewTarget(): Promise<RecordingTargetV2> { 60 throw new RecordingError( 61 'Can not create a new Chrome target.' + 62 'All Chrome targets are created at factory initialisation.'); 63 } 64 65 getName(): string { 66 return 'Chrome'; 67 } 68 69 listRecordingProblems(): string[] { 70 const recordingProblems = []; 71 if (!this.isExtensionInstalled) { 72 recordingProblems.push(EXTENSION_NOT_INSTALLED); 73 } 74 return recordingProblems; 75 } 76 77 listTargets(): RecordingTargetV2[] { 78 return this.targets; 79 } 80 81 setOnTargetChange(onTargetChange: OnTargetChangeCallback): void { 82 for (const target of this.targets) { 83 target.onTargetChange = onTargetChange; 84 } 85 } 86} 87 88// We only instantiate the factory if Perfetto UI is open in the Chrome browser. 89if (window.chrome && chrome.runtime) { 90 targetFactoryRegistry.register(new ChromeTargetFactory()); 91} 92