diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index d74d3d57ed..979b623698 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -400,6 +400,7 @@ "statusBar.noFolderBackground": "#10192c", "statusBar.debuggingBackground": "#10192c", // "statusBar.foreground": "", + "statusBarItem.hostBackground": "#0063a5", "statusBarItem.prominentBackground": "#0063a5", "statusBarItem.prominentHoverBackground": "#0063a5dd", // "statusBarItem.activeBackground": "", diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index e974741261..0b20650d8d 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -4,7 +4,8 @@ "include": "./hc_black_defaults.json", "colors": { "selection.background": "#008000", - "editor.selectionBackground": "#FFFFFF" + "editor.selectionBackground": "#FFFFFF", + "statusBarItem.hostBackground": "#00000000" }, "tokenColors": [ { diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index 932ebabbbc..54211a5b80 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -6,6 +6,7 @@ "editor.foreground": "#FFFFFF", "editorIndentGuide.background": "#FFFFFF", "editorIndentGuide.activeBackground": "#FFFFFF", + "statusBarItem.hostBackground": "#00000000", "sideBarTitle.foreground": "#FFFFFF" }, "settings": [ diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index 3d3267fca8..0d7753ae46 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -27,6 +27,7 @@ "statusBar.background": "#423523", "statusBar.debuggingBackground": "#423523", "statusBar.noFolderBackground": "#423523", + "statusBarItem.hostBackground": "#6e583b", "activityBar.background": "#221a0f", "activityBar.foreground": "#d3af86", "sideBar.background": "#362712", diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index 5fd846e43c..1aad04028e 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -31,6 +31,7 @@ "statusBar.debuggingBackground": "#505050", "statusBar.noFolderBackground": "#505050", "titleBar.activeBackground": "#505050", + "statusBarItem.hostBackground": "#3655b5", "activityBar.background": "#353535", "activityBar.foreground": "#ffffff", "activityBarBadge.background": "#3655b5", diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index ec445f89fd..a203ace74e 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -50,6 +50,7 @@ "statusBar.background": "#414339", "statusBar.noFolderBackground": "#414339", "statusBar.debuggingBackground": "#75715E", + "statusBarItem.hostBackground": "#AC6218", "activityBar.background": "#272822", "activityBar.foreground": "#f8f8f2", "activityBar.dropBackground": "#414339", diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index 2cef3aa273..467c81df9d 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -516,6 +516,7 @@ "statusBar.background": "#705697", "statusBar.noFolderBackground": "#705697", "statusBar.debuggingBackground": "#705697", + "statusBarItem.hostBackground": "#4e3c69", "activityBar.background": "#EDEDF5", "activityBar.foreground": "#705697", "activityBarBadge.background": "#705697", diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 3de9575b0d..31fa8a1b62 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -9,6 +9,7 @@ "sideBar.background": "#330000", "statusBar.background": "#700000", "statusBar.noFolderBackground": "#700000", + "statusBarItem.hostBackground": "#c33", "editorGroupHeader.tabsBackground": "#330000", "titleBar.activeBackground": "#770000", "titleBar.inactiveBackground": "#772222", diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 30cbe306f4..9336e2ca21 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -448,6 +448,7 @@ "statusBar.background": "#00212B", "statusBar.debuggingBackground": "#00212B", "statusBar.noFolderBackground": "#00212B", + "statusBarItem.hostBackground": "#2AA19899", "statusBarItem.prominentBackground": "#003847", "statusBarItem.prominentHoverBackground": "#003847", // "statusBarItem.activeBackground": "", diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index 79caab2000..20e50ab36e 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -447,6 +447,7 @@ "statusBar.debuggingBackground": "#EEE8D5", "statusBar.noFolderBackground": "#EEE8D5", // "statusBar.foreground": "", + "statusBarItem.hostBackground": "#AC9D57", "statusBarItem.prominentBackground": "#DDD6C1", "statusBarItem.prominentHoverBackground": "#DDD6C199", // "statusBarItem.activeBackground": "", diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json index 8d01980349..5236bf4a91 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json @@ -30,6 +30,7 @@ "debugToolBar.background": "#001c40", "titleBar.activeBackground": "#001126", "statusBar.background": "#001126", + "statusBarItem.hostBackground": "#0e639c", "statusBar.noFolderBackground": "#001126", "statusBar.debuggingBackground": "#001126", "activityBar.background": "#001733", diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index e1a0316d5f..f3edba5460 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -141,13 +141,13 @@ export const enum ProtocolConstants { */ AcknowledgeTimeoutTime = 10000, // 10 seconds /** - * Send at least a message every 30s for keep alive reasons. + * Send at least a message every 5s for keep alive reasons. */ - KeepAliveTime = 30000, // 30 seconds + KeepAliveTime = 5000, // 5 seconds /** - * If there is no message received for 60 seconds, consider the connection closed... + * If there is no message received for 10 seconds, consider the connection closed... */ - KeepAliveTimeoutTime = 60000, // 60 seconds + KeepAliveTimeoutTime = 10000, // 10 seconds /** * If there is no reconnection within this time-frame, consider the connection permanently closed... */ diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts index 3a774fd4bb..36fa0a0f36 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts @@ -1770,7 +1770,9 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { @INotificationService private readonly notificationService: INotificationService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IOpenerService private readonly openerService: IOpenerService, - @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService + @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService ) { super(id, label, 'extension-action'); this.recommendations = recommendations; @@ -1787,17 +1789,30 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { let installPromises: Promise[] = []; let model = new PagedModel(pager); for (let i = 0; i < pager.total; i++) { - installPromises.push(model.resolve(i, CancellationToken.None).then(e => { - return this.extensionWorkbenchService.install(e).then(undefined, err => { - console.error(err); - return promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.identifier.id), err, this.instantiationService, this.notificationService, this.openerService); - }); - })); + installPromises.push(model.resolve(i, CancellationToken.None).then(e => this.installExtension(e))); } return Promise.all(installPromises); }); }); } + + private async installExtension(extension: IExtension): Promise { + try { + if (extension.local && extension.gallery) { + if (isUIExtension(extension.local.manifest, this.configurationService)) { + await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(extension.gallery); + return; + } else if (this.extensionManagementServerService.remoteExtensionManagementServer) { + await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(extension.gallery); + return; + } + } + await this.extensionWorkbenchService.install(extension); + } catch (err) { + console.error(err); + return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.identifier.id), err, this.instantiationService, this.notificationService, this.openerService); + } + } } export class InstallRecommendedExtensionAction extends Action { diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 319f152ccf..1a2d60d96d 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -274,22 +274,26 @@ export class RemoteFileDialog { }); this.filePickBox.onDidChangeValue(async value => { - // onDidChangeValue can also be triggered by the auto complete, so if it looks like the auto complete, don't do anything - if (this.isChangeFromUser()) { - // If the user has just entered more bad path, don't change anything - if (!equalsIgnoreCase(value, this.constructFullUserPath()) && !this.isBadSubpath(value)) { - this.filePickBox.validationMessage = undefined; - const valueUri = this.remoteUriFrom(this.trimTrailingSlash(this.filePickBox.value)); - let updated: UpdateResult = UpdateResult.NotUpdated; - if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), valueUri, true)) { - updated = await this.tryUpdateItems(value, this.remoteUriFrom(this.filePickBox.value)); + try { + // onDidChangeValue can also be triggered by the auto complete, so if it looks like the auto complete, don't do anything + if (this.isChangeFromUser()) { + // If the user has just entered more bad path, don't change anything + if (!equalsIgnoreCase(value, this.constructFullUserPath()) && !this.isBadSubpath(value)) { + this.filePickBox.validationMessage = undefined; + const valueUri = this.remoteUriFrom(this.trimTrailingSlash(this.filePickBox.value)); + let updated: UpdateResult = UpdateResult.NotUpdated; + if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), valueUri, true)) { + updated = await this.tryUpdateItems(value, this.remoteUriFrom(this.filePickBox.value)); + } + if (updated === UpdateResult.NotUpdated) { + this.setActiveItems(value); + } + } else { + this.filePickBox.activeItems = []; } - if (updated === UpdateResult.NotUpdated) { - this.setActiveItems(value); - } - } else { - this.filePickBox.activeItems = []; } + } catch { + // Since any text can be entered in the input box, there is potential for error causing input. If this happens, do nothing. } }); this.filePickBox.onDidHide(() => { @@ -331,12 +335,13 @@ export class RemoteFileDialog { this.filePickBox.busy = true; let resolveValue: URI | undefined; let navigateValue: URI | undefined; - const trimmedPickBoxValue = ((this.filePickBox.value.length > 1) && this.endsWithSlash(this.filePickBox.value)) ? this.filePickBox.value.substr(0, this.filePickBox.value.length - 1) : this.filePickBox.value; - const inputUri = this.remoteUriFrom(trimmedPickBoxValue); - const inputUriDirname = resources.dirname(inputUri); + let inputUri: URI | undefined; + let inputUriDirname: URI | undefined; let stat: IFileStat | undefined; let statDirname: IFileStat | undefined; try { + inputUri = resources.removeTrailingPathSeparator(this.remoteUriFrom(this.filePickBox.value)); + inputUriDirname = resources.dirname(inputUri); statDirname = await this.fileService.resolve(inputUriDirname); stat = await this.fileService.resolve(inputUri); } catch (e) { @@ -363,17 +368,18 @@ export class RemoteFileDialog { } } - if (resolveValue) { - resolveValue = this.addPostfix(resolveValue); + + if (navigateValue) { + // Try to navigate into the folder. + await this.updateItems(navigateValue, true, this.trailing); + } else { + if (resolveValue) { + resolveValue = this.addPostfix(resolveValue); + } if (await this.validate(resolveValue)) { this.filePickBox.busy = false; return resolveValue; } - } else if (navigateValue) { - // Try to navigate into the folder. - await this.updateItems(navigateValue, true, this.trailing); - } else { - // validation error. Path does not exist. } this.filePickBox.busy = false; return undefined; @@ -575,7 +581,12 @@ export class RemoteFileDialog { }); } - private async validate(uri: URI): Promise { + private async validate(uri: URI | undefined): Promise { + if (uri === undefined) { + this.filePickBox.validationMessage = nls.localize('remoteFileDialog.invalidPath', 'Please enter a valid path.'); + return Promise.resolve(false); + } + let stat: IFileStat | undefined; let statDirname: IFileStat | undefined; try { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 184721489a..624d8e1adc 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -9,6 +9,7 @@ import { realpathSync } from 'vs/base/node/extpath'; import { IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { Schemas } from 'vs/base/common/network'; export class ExtensionHostProfiler { @@ -30,7 +31,9 @@ export class ExtensionHostProfiler { private distill(profile: Profile, extensions: IExtensionDescription[]): IExtensionHostProfile { let searchTree = TernarySearchTree.forPaths(); for (let extension of extensions) { - searchTree.set(realpathSync(extension.extensionLocation.fsPath), extension); + if (extension.extensionLocation.scheme === Schemas.file) { + searchTree.set(realpathSync(extension.extensionLocation.fsPath), extension); + } } let nodes = profile.nodes; diff --git a/src/vs/workbench/services/files/common/fileService.ts b/src/vs/workbench/services/files/common/fileService.ts index 15d3f7ed76..0ee4793ddc 100644 --- a/src/vs/workbench/services/files/common/fileService.ts +++ b/src/vs/workbench/services/files/common/fileService.ts @@ -338,8 +338,9 @@ export class FileService extends Disposable implements IFileService { // but to the same length. This is a compromise we take to avoid having to produce checksums of // the file content for comparison which would be much slower to compute. if ( - options && typeof options.mtime === 'number' && typeof options.etag === 'string' && - options.etag !== ETAG_DISABLED && options.mtime < stat.mtime && options.etag !== etag({ mtime: options.mtime /* not using stat.mtime for a reason, see above */, size: stat.size }) + options && typeof options.mtime === 'number' && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED && + typeof stat.mtime === 'number' && typeof stat.size === 'number' && + options.mtime < stat.mtime && options.etag !== etag({ mtime: options.mtime /* not using stat.mtime for a reason, see above */, size: stat.size }) ) { throw new FileOperationError(localize('fileModifiedError', "File Modified Since"), FileOperationResult.FILE_MODIFIED_SINCE, options); } @@ -496,7 +497,7 @@ export class FileService extends Disposable implements IFileService { } // Return early if file not modified since (unless disabled) - if (options && options.etag !== ETAG_DISABLED && options.etag === stat.etag) { + if (options && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED && options.etag === stat.etag) { throw new FileOperationError(localize('fileNotModifiedError', "File not modified since"), FileOperationResult.FILE_NOT_MODIFIED_SINCE, options); } diff --git a/src/vs/workbench/services/files/test/node/diskFileService.test.ts b/src/vs/workbench/services/files/test/node/diskFileService.test.ts index 29d19503eb..19e141d72d 100644 --- a/src/vs/workbench/services/files/test/node/diskFileService.test.ts +++ b/src/vs/workbench/services/files/test/node/diskFileService.test.ts @@ -15,7 +15,7 @@ import { getPathFromAmdModule } from 'vs/base/common/amd'; import { copy, rimraf, symlink, RimRafMode, rimrafSync } from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync } from 'fs'; -import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag } from 'vs/platform/files/common/files'; +import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat } from 'vs/platform/files/common/files'; import { NullLogService } from 'vs/platform/log/common/log'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -62,6 +62,8 @@ export class TestDiskFileSystemProvider extends DiskFileSystemProvider { totalBytesRead: number = 0; + private invalidStatSize: boolean; + private _testCapabilities: FileSystemProviderCapabilities; get capabilities(): FileSystemProviderCapabilities { if (!this._testCapabilities) { @@ -82,6 +84,20 @@ export class TestDiskFileSystemProvider extends DiskFileSystemProvider { this._testCapabilities = capabilities; } + setInvalidStatSize(disabled: boolean): void { + this.invalidStatSize = disabled; + } + + async stat(resource: URI): Promise { + const res = await super.stat(resource); + + if (this.invalidStatSize) { + res.size = String(res.size) as any; // for https://github.com/Microsoft/vscode/issues/72909 + } + + return res; + } + async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { const bytesRead = await super.read(fd, pos, data, offset, length); @@ -1049,6 +1065,24 @@ suite('Disk File Service', () => { assert.equal(fileProvider.totalBytesRead, 0); }); + test('readFile - FILE_NOT_MODIFIED_SINCE does not fire wrongly - https://github.com/Microsoft/vscode/issues/72909', async () => { + setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); + fileProvider.setInvalidStatSize(true); + + const resource = URI.file(join(testDir, 'index.html')); + + await service.readFile(resource); + + let error: FileOperationError | undefined = undefined; + try { + await service.readFile(resource, { etag: undefined }); + } catch (err) { + error = err; + } + + assert.ok(!error); + }); + test('readFile - FILE_NOT_MODIFIED_SINCE - unbuffered', async () => { setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite);