| // Copyright 2023 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as Common from '../../../core/common/common.js'; |
| |
| export type Screenshot = string&{_brand: 'ImageData'}; |
| |
| export interface ScreenshotMetaData { |
| recordingName: string; |
| index: number; |
| data: Screenshot; |
| } |
| |
| let instance: ScreenshotStorage|null = null; |
| |
| // Default size of storage |
| const DEFAULT_MAX_STORAGE_SIZE = 50 * 1024 * 1024; |
| |
| /** |
| * This class stores the screenshots taken for a specific recording |
| * in a settings object. The total storage size is limited to 50 MB |
| * by default and the least recently accessed screenshots will be |
| * deleted first. |
| */ |
| export class ScreenshotStorage { |
| #screenshotSettings: Common.Settings.Setting<ScreenshotMetaData[]>; |
| #screenshots: Map<string, ScreenshotMetaData>; |
| #maxStorageSize: number; |
| |
| constructor(maxStorageSize = DEFAULT_MAX_STORAGE_SIZE) { |
| this.#screenshotSettings = Common.Settings.Settings.instance().createSetting( |
| 'recorder_screenshots', |
| [], |
| ); |
| this.#screenshots = this.#loadFromSettings(); |
| this.#maxStorageSize = maxStorageSize; |
| } |
| |
| clear(): void { |
| this.#screenshotSettings.set([]); |
| this.#screenshots = new Map(); |
| } |
| |
| getScreenshotForSection( |
| recordingName: string, |
| index: number, |
| ): Screenshot|null { |
| const screenshot = this.#screenshots.get( |
| this.#calculateKey(recordingName, index), |
| ); |
| if (!screenshot) { |
| return null; |
| } |
| |
| this.#syncWithSettings(screenshot); |
| return screenshot.data; |
| } |
| |
| storeScreenshotForSection( |
| recordingName: string, |
| index: number, |
| data: Screenshot, |
| ): void { |
| const screenshot = {recordingName, index, data}; |
| this.#screenshots.set(this.#calculateKey(recordingName, index), screenshot); |
| this.#syncWithSettings(screenshot); |
| } |
| |
| deleteScreenshotsForRecording(recordingName: string): void { |
| for (const [key, entry] of this.#screenshots) { |
| if (entry.recordingName === recordingName) { |
| this.#screenshots.delete(key); |
| } |
| } |
| |
| this.#syncWithSettings(); |
| } |
| |
| #calculateKey(recordingName: string, index: number): string { |
| return `${recordingName}:${index}`; |
| } |
| |
| #loadFromSettings(): Map<string, ScreenshotMetaData> { |
| const screenshots = new Map<string, ScreenshotMetaData>(); |
| const data = this.#screenshotSettings.get(); |
| for (const item of data) { |
| screenshots.set(this.#calculateKey(item.recordingName, item.index), item); |
| } |
| |
| return screenshots; |
| } |
| |
| #syncWithSettings(modifiedScreenshot?: ScreenshotMetaData): void { |
| if (modifiedScreenshot) { |
| const key = this.#calculateKey( |
| modifiedScreenshot.recordingName, |
| modifiedScreenshot.index, |
| ); |
| |
| // Make sure that the modified screenshot is moved to the end of the map |
| // as the JS Map remembers the original insertion order of the keys. |
| this.#screenshots.delete(key); |
| this.#screenshots.set(key, modifiedScreenshot); |
| } |
| |
| const screenshots = []; |
| let currentStorageSize = 0; |
| |
| // Take screenshots from the end of the list until the size constraint is met. |
| for (const [key, screenshot] of Array |
| .from( |
| this.#screenshots.entries(), |
| ) |
| .reverse()) { |
| if (currentStorageSize < this.#maxStorageSize) { |
| currentStorageSize += screenshot.data.length; |
| screenshots.push(screenshot); |
| } else { |
| // Delete all screenshots that exceed the storage limit. |
| this.#screenshots.delete(key); |
| } |
| } |
| |
| this.#screenshotSettings.set(screenshots.reverse()); |
| } |
| |
| static instance( |
| opts: { |
| forceNew?: boolean|null, |
| maxStorageSize?: number, |
| } = {forceNew: null, maxStorageSize: DEFAULT_MAX_STORAGE_SIZE}, |
| ): ScreenshotStorage { |
| const {forceNew, maxStorageSize} = opts; |
| if (!instance || forceNew) { |
| instance = new ScreenshotStorage(maxStorageSize); |
| } |
| return instance; |
| } |
| } |