[ui] Add screenshot track
Add ui support for visualizing screenshots in traces. In followup
change we will add a python script for capturing trace and screencapture
video simultaneously and then merging into a single trace. Using the
traces captured using the script and this UI feature we should be able
to see screenshots in perfetto like this: http://screen/BcWEtzCfAt7kb6m.
Bug: b/287934064
Change-Id: I01e528a9e12b8ceab72fdbec1f1ece9ce977a5cc
diff --git a/Android.bp b/Android.bp
index f66c143..4560f9f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5370,6 +5370,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -7969,6 +7970,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -8002,6 +8004,7 @@
"external/perfetto/protos/perfetto/trace/track_event/log_message.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.gen.cc",
+ "external/perfetto/protos/perfetto/trace/track_event/screenshot.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/source_location.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/task_execution.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.gen.cc",
@@ -8035,6 +8038,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -8068,6 +8072,7 @@
"external/perfetto/protos/perfetto/trace/track_event/log_message.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.gen.h",
+ "external/perfetto/protos/perfetto/trace/track_event/screenshot.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/source_location.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/task_execution.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.gen.h",
@@ -8105,6 +8110,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -8145,6 +8151,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -8177,6 +8184,7 @@
"external/perfetto/protos/perfetto/trace/track_event/log_message.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pb.cc",
+ "external/perfetto/protos/perfetto/trace/track_event/screenshot.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/source_location.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/task_execution.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pb.cc",
@@ -8210,6 +8218,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -8242,6 +8251,7 @@
"external/perfetto/protos/perfetto/trace/track_event/log_message.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pb.h",
+ "external/perfetto/protos/perfetto/trace/track_event/screenshot.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/source_location.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/task_execution.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pb.h",
@@ -8279,6 +8289,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -8312,6 +8323,7 @@
"external/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pbzero.cc",
+ "external/perfetto/protos/perfetto/trace/track_event/screenshot.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/source_location.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pbzero.cc",
@@ -8345,6 +8357,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -8378,6 +8391,7 @@
"external/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pbzero.h",
+ "external/perfetto/protos/perfetto/trace/track_event/screenshot.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/source_location.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h",
@@ -8521,6 +8535,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
@@ -10759,6 +10774,7 @@
"src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql",
"src/trace_processor/perfetto_sql/stdlib/android/network_packets.sql",
"src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql",
"src/trace_processor/perfetto_sql/stdlib/android/slices.sql",
"src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_maxsdk28.sql",
"src/trace_processor/perfetto_sql/stdlib/android/startup/internal_startups_minsdk29.sql",
@@ -12176,6 +12192,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
diff --git a/BUILD b/BUILD
index a6e2834..4b3baee 100644
--- a/BUILD
+++ b/BUILD
@@ -2256,6 +2256,7 @@
"src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql",
"src/trace_processor/perfetto_sql/stdlib/android/network_packets.sql",
"src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql",
"src/trace_processor/perfetto_sql/stdlib/android/slices.sql",
"src/trace_processor/perfetto_sql/stdlib/android/statsd.sql",
"src/trace_processor/perfetto_sql/stdlib/android/thread.sql",
@@ -4883,6 +4884,7 @@
"protos/perfetto/trace/track_event/log_message.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
+ "protos/perfetto/trace/track_event/screenshot.proto",
"protos/perfetto/trace/track_event/source_location.proto",
"protos/perfetto/trace/track_event/task_execution.proto",
"protos/perfetto/trace/track_event/thread_descriptor.proto",
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 75043d0..dff8d0b 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -10661,6 +10661,14 @@
// End of protos/perfetto/trace/track_event/chrome_window_handle_event_info.proto
+// Begin of protos/perfetto/trace/track_event/screenshot.proto
+
+message Screenshot {
+ optional bytes jpg_image = 1;
+}
+
+// End of protos/perfetto/trace/track_event/screenshot.proto
+
// Begin of protos/perfetto/trace/track_event/task_execution.proto
// TrackEvent arguments describing the execution of a task.
@@ -10738,7 +10746,7 @@
// their default track association) can be emitted as part of a
// TrackEventDefaults message.
//
-// Next reserved id: 13 (up to 15). Next id: 50.
+// Next reserved id: 13 (up to 15). Next id: 51.
message TrackEvent {
// Names of categories of the event. In the client library, categories are a
// way to turn groups of individual events on or off.
@@ -10891,6 +10899,7 @@
optional ChromeContentSettingsEventInfo chrome_content_settings_event_info =
43;
optional ChromeActiveProcesses chrome_active_processes = 49;
+ optional Screenshot screenshot = 50;
// This field is used only if the source location represents the function that
// executes during this event.
diff --git a/protos/perfetto/trace/track_event/BUILD.gn b/protos/perfetto/trace/track_event/BUILD.gn
index 7dc1aad..c101c46 100644
--- a/protos/perfetto/trace/track_event/BUILD.gn
+++ b/protos/perfetto/trace/track_event/BUILD.gn
@@ -37,6 +37,7 @@
"log_message.proto",
"process_descriptor.proto",
"range_of_interest.proto",
+ "screenshot.proto",
"source_location.proto",
"task_execution.proto",
"thread_descriptor.proto",
diff --git a/protos/perfetto/trace/track_event/screenshot.proto b/protos/perfetto/trace/track_event/screenshot.proto
new file mode 100644
index 0000000..02e658c
--- /dev/null
+++ b/protos/perfetto/trace/track_event/screenshot.proto
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+message Screenshot {
+ optional bytes jpg_image = 1;
+}
diff --git a/protos/perfetto/trace/track_event/track_event.proto b/protos/perfetto/trace/track_event/track_event.proto
index cde2fbb..325465d 100644
--- a/protos/perfetto/trace/track_event/track_event.proto
+++ b/protos/perfetto/trace/track_event/track_event.proto
@@ -33,6 +33,7 @@
import "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto";
import "protos/perfetto/trace/track_event/chrome_user_event.proto";
import "protos/perfetto/trace/track_event/chrome_window_handle_event_info.proto";
+import "protos/perfetto/trace/track_event/screenshot.proto";
import "protos/perfetto/trace/track_event/source_location.proto";
package perfetto.protos;
@@ -102,7 +103,7 @@
// their default track association) can be emitted as part of a
// TrackEventDefaults message.
//
-// Next reserved id: 13 (up to 15). Next id: 50.
+// Next reserved id: 13 (up to 15). Next id: 51.
message TrackEvent {
// Names of categories of the event. In the client library, categories are a
// way to turn groups of individual events on or off.
@@ -255,6 +256,7 @@
optional ChromeContentSettingsEventInfo chrome_content_settings_event_info =
43;
optional ChromeActiveProcesses chrome_active_processes = 49;
+ optional Screenshot screenshot = 50;
// This field is used only if the source location represents the function that
// executes during this event.
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 504e66f..06e5c39 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -21,6 +21,7 @@
#include <string>
#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/base64.h"
#include "perfetto/ext/base/string_writer.h"
#include "perfetto/trace_processor/status.h"
#include "src/trace_processor/importers/common/args_tracker.h"
@@ -122,6 +123,10 @@
storage_.InternString(base::StringView(key.key)),
Variadic::Boolean(value));
}
+ void AddBytes(const Key& key, const protozero::ConstBytes& value) final {
+ std::string b64_data = base::Base64Encode(value.data, value.size);
+ AddString(key, b64_data);
+ }
bool AddJson(const Key& key, const protozero::ConstChars& value) final {
auto json_value = json::ParseJsonString(value);
if (!json_value)
diff --git a/src/trace_processor/importers/proto/track_event_parser.h b/src/trace_processor/importers/proto/track_event_parser.h
index a1cd114..6198dd5 100644
--- a/src/trace_processor/importers/proto/track_event_parser.h
+++ b/src/trace_processor/importers/proto/track_event_parser.h
@@ -44,8 +44,8 @@
//
// TODO(ddrone): replace with a predicate on field id to import new fields
// automatically
-static constexpr uint16_t kReflectFields[] = {24, 25, 26, 27, 28, 29, 32, 33,
- 34, 35, 38, 39, 40, 41, 43, 49};
+static constexpr uint16_t kReflectFields[] = {
+ 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 43, 49, 50};
class PacketSequenceStateGeneration;
class TraceProcessorContext;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
index 8a70e98..7567f75 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
@@ -25,6 +25,7 @@
"monitor_contention.sql",
"network_packets.sql",
"process_metadata.sql",
+ "screenshots.sql",
"slices.sql",
"statsd.sql",
"thread.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql b/src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql
new file mode 100644
index 0000000..7efc08a
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql
@@ -0,0 +1,32 @@
+-- Copyright 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
+--
+-- https://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.
+
+-- Screenshot slices, used in perfetto UI.
+--
+-- @column id Slice id.
+-- @column ts Slice timestamp.
+-- @column dur Slice duration, should be typically 0 since
+-- screeenshot slices are of instant type.
+-- @column name Slice name.
+CREATE PERFETTO TABLE android_screenshots AS
+SELECT
+ slice.id as id,
+ slice.ts as ts,
+ slice.dur as dur,
+ slice.name as name
+FROM slice
+JOIN args USING(arg_set_id)
+WHERE slice.name = "Screenshot"
+ AND slice.category = "android_screenshot"
+ AND args.key = "screenshot.jpg_image";
diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss
index 70702dc..3b9b236 100644
--- a/ui/src/assets/details.scss
+++ b/ui/src/assets/details.scss
@@ -700,3 +700,10 @@
}
}
}
+
+.screenshot-panel {
+ height: 100%;
+ img {
+ max-height: 100%;
+ }
+}
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 9ccf890..d64c00b 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -66,6 +66,9 @@
PROCESS_SCHEDULING_TRACK_KIND,
} from '../tracks/process_scheduling';
import {PROCESS_SUMMARY_TRACK} from '../tracks/process_summary';
+import {
+ decideTracks as screenshotDecideTracks,
+} from '../tracks/screenshots';
import {THREAD_STATE_TRACK_KIND} from '../tracks/thread_state';
import {THREAD_STATE_TRACK_V2_KIND} from '../tracks/thread_state_v2';
@@ -1955,6 +1958,14 @@
async decideTracks(): Promise<DeferredAction[]> {
await this.defineMaxLayoutDepthSqlFunction();
+ {
+ const result = screenshotDecideTracks(this.engine);
+ if (result !== null) {
+ const {tracksToAdd} = await result;
+ this.tracksToAdd.push(...tracksToAdd);
+ }
+ }
+
// Add first the global tracks that don't require per-process track groups.
await this.addCpuSchedulingTracks();
await this.addFtraceTrack(
diff --git a/ui/src/tracks/screenshots/index.ts b/ui/src/tracks/screenshots/index.ts
new file mode 100644
index 0000000..63ff916
--- /dev/null
+++ b/ui/src/tracks/screenshots/index.ts
@@ -0,0 +1,94 @@
+// 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 {v4 as uuidv4} from 'uuid';
+
+import {AddTrackArgs} from '../../common/actions';
+import {Engine} from '../../common/engine';
+import {PrimaryTrackSortKey} from '../../common/state';
+import {
+ NamedSliceTrackTypes,
+} from '../../frontend/named_slice_track';
+import {NewTrackArgs, Track} from '../../frontend/track';
+import {Plugin, PluginContext, PluginInfo} from '../../public';
+import {
+ CustomSqlDetailsPanelConfig,
+ CustomSqlTableDefConfig,
+ CustomSqlTableSliceTrack,
+} from '../custom_sql_table_slices';
+
+import {
+ ScreenshotTab,
+} from './screenshot_panel';
+
+export {Data} from '../chrome_slices';
+
+class ScreenshotsTrack extends CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
+ static readonly kind = 'dev.perfetto.ScreenshotsTrack';
+ static create(args: NewTrackArgs): Track {
+ return new ScreenshotsTrack(args);
+ }
+
+ getSqlDataSource(): CustomSqlTableDefConfig {
+ return {
+ sqlTableName: 'android_screenshots',
+ columns: ['*'],
+ };
+ }
+
+ getDetailsPanel(): CustomSqlDetailsPanelConfig {
+ return {
+ kind: ScreenshotTab.kind,
+ config: {
+ sqlTableName: this.tableName,
+ title: 'Screenshots',
+ },
+ };
+ }
+}
+
+export type DecideTracksResult = {
+ tracksToAdd: AddTrackArgs[],
+};
+
+export async function decideTracks(engine: Engine):
+ Promise<DecideTracksResult> {
+ const result: DecideTracksResult = {
+ tracksToAdd: [],
+ };
+
+ await engine.query(`SELECT IMPORT('android.screenshots')`);
+
+ result.tracksToAdd.push({
+ id: uuidv4(),
+ engineId: engine.id,
+ kind: ScreenshotsTrack.kind,
+ trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+ name: 'Screenshots',
+ config: {},
+ trackGroup: undefined,
+ });
+ return result;
+}
+
+class ScreenshotsPlugin implements Plugin {
+ onActivate(ctx: PluginContext): void {
+ ctx.registerTrack(ScreenshotsTrack);
+ }
+}
+
+export const plugin: PluginInfo = {
+ pluginId: 'perfetto.Screenshots',
+ plugin: ScreenshotsPlugin,
+};
diff --git a/ui/src/tracks/screenshots/screenshot_panel.ts b/ui/src/tracks/screenshots/screenshot_panel.ts
new file mode 100644
index 0000000..350357f
--- /dev/null
+++ b/ui/src/tracks/screenshots/screenshot_panel.ts
@@ -0,0 +1,70 @@
+// 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 {assertTrue} from '../../base/logging';
+import {exists} from '../../base/utils';
+import {EngineProxy} from '../../common/engine';
+import {
+ BottomTab,
+ bottomTabRegistry,
+ NewBottomTabArgs,
+} from '../../frontend/bottom_tab';
+import {
+ GenericSliceDetailsTabConfig,
+} from '../../frontend/generic_slice_details_tab';
+import {getSlice, SliceDetails} from '../../frontend/sql/slice';
+import {asSliceSqlId} from '../../frontend/sql_types';
+
+async function getSliceDetails(
+ engine: EngineProxy, id: number): Promise<SliceDetails|undefined> {
+ return getSlice(engine, asSliceSqlId(id));
+}
+
+export class ScreenshotTab extends BottomTab<GenericSliceDetailsTabConfig> {
+ static readonly kind = 'dev.perfetto.ScreenshotDetailsPanel';
+
+ private sliceDetails?: SliceDetails;
+
+ static create(args: NewBottomTabArgs): ScreenshotTab {
+ return new ScreenshotTab(args);
+ }
+
+ constructor(args: NewBottomTabArgs) {
+ super(args);
+ getSliceDetails(this.engine, this.config.id)
+ .then((sliceDetails) => this.sliceDetails = sliceDetails);
+ }
+
+ renderTabCanvas() {}
+
+ getTitle() {
+ return this.config.title;
+ }
+
+ viewTab() {
+ if (!exists(this.sliceDetails) || !exists(this.sliceDetails.args) ||
+ this.sliceDetails.args.length == 0) {
+ return m('h2', 'Loading Screenshot');
+ }
+ assertTrue(this.sliceDetails.args[0].key == 'screenshot.jpg_image');
+ return m('.screenshot-panel', m('img', {
+ src: 'data:image/png;base64, ' +
+ this.sliceDetails.args[0].displayValue,
+ }));
+ }
+}
+
+bottomTabRegistry.register(ScreenshotTab);