diff --git a/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts new file mode 100644 index 0000000000000000000000000000000000000000..31c389f4e503f52396b93d6c1faadc16a16763da --- /dev/null +++ b/src/vs/workbench/services/log/browser/indexedDBLogProvider.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType, FileSystemProviderErrorCode } from 'vs/platform/files/common/files'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { FileSystemError } from 'vs/workbench/api/common/extHostTypes'; + +const LOGS_OBJECT_STORE = 'logs'; +export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd'; + +export class IndexedDBLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability { + + readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + readonly onDidChangeCapabilities: Event = Event.None; + + private readonly _onDidChangeFile: Emitter = this._register(new Emitter()); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private readonly versions: Map = new Map(); + + private readonly database: Promise; + + constructor( + ) { + super(); + this.database = this.openDatabase(2); + } + + private openDatabase(version: number): Promise { + return new Promise((c, e) => { + const request = window.indexedDB.open('LoggingDatabase', version); + request.onerror = (err) => e(request.error); + request.onsuccess = () => { + const db = request.result; + if (db.objectStoreNames.contains(LOGS_OBJECT_STORE)) { + c(db); + } + }; + request.onupgradeneeded = (e) => { + const db = request.result; + if (!db.objectStoreNames.contains(LOGS_OBJECT_STORE)) { + db.createObjectStore(LOGS_OBJECT_STORE); + } + c(db); + }; + }); + } + + watch(resource: URI, opts: IWatchOptions): IDisposable { + return Disposable.None; + } + + mkdir(resource: URI): Promise { + return Promise.reject(new Error('Not Supported')); + } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + return Promise.reject(new Error('Not Supported')); + } + + readdir(resource: URI): Promise<[string, FileType][]> { + return Promise.reject(new Error('Not Supported')); + } + + delete(resource: URI, opts: FileDeleteOptions): Promise { + return Promise.reject(new Error('Not Supported')); + } + + async stat(resource: URI): Promise { + try { + const content = await this.readFile(resource); + return { + type: FileType.File, + ctime: 0, + mtime: this.versions.get(resource.toString()) || 0, + size: content.byteLength + }; + } catch (e) { + return { + type: FileType.File, + ctime: 0, + mtime: 0, + size: 0 + }; + } + } + + async readFile(resource: URI): Promise { + return new Promise(async (c, e) => { + const db = await this.database; + const transaction = db.transaction([LOGS_OBJECT_STORE]); + const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const request = objectStore.get(resource.path); + request.onerror = () => e(request.error); + request.onsuccess = () => { + if (request.result) { + c(VSBuffer.fromString(request.result).buffer); + } else { + e(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound)); + } + }; + }); + } + + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + return new Promise(async (c, e) => { + const db = await this.database; + const transaction = db.transaction([LOGS_OBJECT_STORE], 'readwrite'); + const objectStore = transaction.objectStore(LOGS_OBJECT_STORE); + const request = objectStore.put(VSBuffer.wrap(content).toString(), resource.path); + request.onerror = () => e(request.error); + request.onsuccess = () => { + this.versions.set(resource.toString(), (this.versions.get(resource.toString()) || 0) + 1); + this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]); + c(); + }; + }); + } + +}