Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 (#14883)

* Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8

* Bump distro

* Upgrade GCC to 4.9 due to yarn install errors

* Update build image

* Fix bootstrap base url

* Bump distro

* Fix build errors

* Update source map file

* Disable checkbox for blocking migration issues (#15131)

* disable checkbox for blocking issues

* wip

* disable checkbox fixes

* fix strings

* Remove duplicate tsec command

* Default to off for tab color if settings not present

* re-skip failing tests

* Fix mocha error

* Bump sqlite version & fix notebooks search view

* Turn off esbuild warnings

* Update esbuild log level

* Fix overflowactionbar tests

* Fix ts-ignore in dropdown tests

* cleanup/fixes

* Fix hygiene

* Bundle in entire zone.js module

* Remove extra constructor param

* bump distro for web compile break

* bump distro for web compile break v2

* Undo log level change

* New distro

* Fix integration test scripts

* remove the "no yarn.lock changes" workflow

* fix scripts v2

* Update unit test scripts

* Ensure ads-kerberos2 updates in .vscodeignore

* Try fix unit tests

* Upload crash reports

* remove nogpu

* always upload crashes

* Use bash script

* Consolidate data/ext dir names

* Create in tmp directory

Co-authored-by: chlafreniere <hichise@gmail.com>
Co-authored-by: Christopher Suh <chsuh@microsoft.com>
Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
Karl Burtram
2021-04-27 14:01:59 -07:00
committed by GitHub
parent 7e1c0076ba
commit 867a963882
1817 changed files with 81812 additions and 50843 deletions

View File

@@ -9,6 +9,9 @@ import { isUNC } from 'vs/base/common/extpath';
import { Schemas } from 'vs/base/common/network';
import { sep } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { IHeaders } from 'vs/base/parts/request/common/request';
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRequestService } from 'vs/platform/request/common/request';
import { getWebviewContentMimeType } from 'vs/platform/webview/common/mimeTypes';
@@ -17,39 +20,99 @@ import { getWebviewContentMimeType } from 'vs/platform/webview/common/mimeTypes'
export const webviewPartitionId = 'webview';
export namespace WebviewResourceResponse {
export enum Type { Success, Failed, AccessDenied }
export enum Type { Success, Failed, AccessDenied, NotModified }
export class StreamSuccess {
readonly type = Type.Success;
constructor(
public readonly stream: VSBufferReadableStream,
public readonly mimeType: string
public readonly etag: string | undefined,
public readonly mimeType: string,
) { }
}
export const Failed = { type: Type.Failed } as const;
export const AccessDenied = { type: Type.AccessDenied } as const;
export type StreamResponse = StreamSuccess | typeof Failed | typeof AccessDenied;
export class NotModified {
readonly type = Type.NotModified;
constructor(
public readonly mimeType: string,
) { }
}
export type StreamResponse = StreamSuccess | typeof Failed | typeof AccessDenied | NotModified;
}
interface FileReader {
readFileStream(resource: URI): Promise<VSBufferReadableStream>;
export namespace WebviewFileReadResponse {
export enum Type { Success, NotModified }
export class StreamSuccess {
readonly type = Type.Success;
constructor(
public readonly stream: VSBufferReadableStream,
public readonly etag: string | undefined
) { }
}
export const NotModified = { type: Type.NotModified } as const;
export type Response = StreamSuccess | typeof NotModified;
}
/**
* Wraps a call to `IFileService.readFileStream` and converts the result to a `WebviewFileReadResponse.Response`
*/
export async function readFileStream(
fileService: IFileService,
resource: URI,
etag: string | undefined,
): Promise<WebviewFileReadResponse.Response> {
try {
const result = await fileService.readFileStream(resource, { etag });
return new WebviewFileReadResponse.StreamSuccess(result.value, result.etag);
} catch (e) {
if (e instanceof FileOperationError) {
const result = e.fileOperationResult;
// NotModified status is expected and can be handled gracefully
if (result === FileOperationResult.FILE_NOT_MODIFIED_SINCE) {
return WebviewFileReadResponse.NotModified;
}
}
// Otherwise the error is unexpected. Re-throw and let caller handle it
throw e;
}
}
export interface WebviewResourceFileReader {
readFileStream(resource: URI, etag: string | undefined): Promise<WebviewFileReadResponse.Response>;
}
export async function loadLocalResource(
requestUri: URI,
ifNoneMatch: string | undefined,
options: {
extensionLocation: URI | undefined;
roots: ReadonlyArray<URI>;
remoteConnectionData?: IRemoteConnectionData | null;
rewriteUri?: (uri: URI) => URI,
},
fileReader: FileReader,
fileReader: WebviewResourceFileReader,
requestService: IRequestService,
logService: ILogService,
token: CancellationToken,
): Promise<WebviewResourceResponse.StreamResponse> {
logService.debug(`loadLocalResource - being. requestUri=${requestUri}`);
let resourceToLoad = getResourceToLoad(requestUri, options.roots);
logService.debug(`loadLocalResource - found resource to load. requestUri=${requestUri}, resourceToLoad=${resourceToLoad}`);
if (!resourceToLoad) {
return WebviewResourceResponse.AccessDenied;
}
@@ -62,18 +125,43 @@ export async function loadLocalResource(
}
if (resourceToLoad.scheme === Schemas.http || resourceToLoad.scheme === Schemas.https) {
const response = await requestService.request({ url: resourceToLoad.toString(true) }, CancellationToken.None);
const headers: IHeaders = {};
if (ifNoneMatch) {
headers['If-None-Match'] = ifNoneMatch;
}
const response = await requestService.request({
url: resourceToLoad.toString(true),
headers: headers
}, token);
logService.debug(`loadLocalResource - Loaded over http(s). requestUri=${requestUri}, response=${response.res.statusCode}`);
if (response.res.statusCode === 200) {
return new WebviewResourceResponse.StreamSuccess(response.stream, mime);
return new WebviewResourceResponse.StreamSuccess(response.stream, response.res.headers['etag'], mime);
}
return WebviewResourceResponse.Failed;
}
try {
const contents = await fileReader.readFileStream(resourceToLoad);
return new WebviewResourceResponse.StreamSuccess(contents, mime);
const contents = await fileReader.readFileStream(resourceToLoad, ifNoneMatch);
logService.debug(`loadLocalResource - Loaded using fileReader. requestUri=${requestUri}`);
switch (contents.type) {
case WebviewFileReadResponse.Type.Success:
return new WebviewResourceResponse.StreamSuccess(contents.stream, contents.etag, mime);
case WebviewFileReadResponse.Type.NotModified:
return new WebviewResourceResponse.NotModified(mime);
default:
logService.error(`loadLocalResource - Unknown file read response`);
return WebviewResourceResponse.Failed;
}
} catch (err) {
logService.debug(`loadLocalResource - Error using fileReader. requestUri=${requestUri}`);
console.log(err);
return WebviewResourceResponse.Failed;
}
}

View File

@@ -19,6 +19,12 @@ export interface WebviewWindowId {
readonly windowId: number;
}
export type WebviewManagerDidLoadResourceResponse =
{ buffer: VSBuffer, etag: string | undefined }
| 'not-modified'
| 'access-denied'
| 'not-found';
export interface IWebviewManagerService {
_serviceBrand: unknown;
@@ -26,7 +32,7 @@ export interface IWebviewManagerService {
unregisterWebview(id: string): Promise<void>;
updateWebviewMetadata(id: string, metadataDelta: Partial<RegisterWebviewMetadata>): Promise<void>;
didLoadResource(requestId: number, content: VSBuffer | undefined): void;
didLoadResource(requestId: number, response: WebviewManagerDidLoadResourceResponse): void;
setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise<void>;
}

View File

@@ -19,7 +19,7 @@ export interface IWebviewPortMapping {
*/
export class WebviewPortMappingManager implements IDisposable {
private readonly _tunnels = new Map<number, Promise<RemoteTunnel>>();
private readonly _tunnels = new Map<number, RemoteTunnel>();
constructor(
private readonly _getExtensionLocation: () => URI | undefined,
@@ -60,19 +60,19 @@ export class WebviewPortMappingManager implements IDisposable {
return undefined;
}
dispose() {
async dispose() {
for (const tunnel of this._tunnels.values()) {
tunnel.then(tunnel => tunnel.dispose());
await tunnel.dispose();
}
this._tunnels.clear();
}
private getOrCreateTunnel(remoteAuthority: IAddress, remotePort: number): Promise<RemoteTunnel> | undefined {
private async getOrCreateTunnel(remoteAuthority: IAddress, remotePort: number): Promise<RemoteTunnel | undefined> {
const existing = this._tunnels.get(remotePort);
if (existing) {
return existing;
}
const tunnel = this.tunnelService.openTunnel({ getAddress: async () => remoteAuthority }, undefined, remotePort);
const tunnel = await this.tunnelService.openTunnel({ getAddress: async () => remoteAuthority }, undefined, remotePort);
if (tunnel) {
this._tunnels.set(remotePort, tunnel);
}

View File

@@ -3,14 +3,15 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { WebContents, webContents } from 'electron';
import { VSBuffer } from 'vs/base/common/buffer';
import { session, WebContents, webContents } from 'electron';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { IRequestService } from 'vs/platform/request/common/request';
import { IWebviewManagerService, RegisterWebviewMetadata, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService';
import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader';
import { IWebviewManagerService, RegisterWebviewMetadata, WebviewManagerDidLoadResourceResponse, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService';
import { WebviewPortMappingProvider } from 'vs/platform/webview/electron-main/webviewPortMappingProvider';
import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
@@ -24,13 +25,23 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
constructor(
@IFileService fileService: IFileService,
@ILogService logService: ILogService,
@IRequestService requestService: IRequestService,
@ITunnelService tunnelService: ITunnelService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
) {
super();
this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, requestService, windowsMainService));
this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, logService, requestService, windowsMainService));
this.portMappingProvider = this._register(new WebviewPortMappingProvider(tunnelService));
const sess = session.fromPartition(webviewPartitionId);
sess.setPermissionRequestHandler((webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback) => {
return callback(false);
});
sess.setPermissionCheckHandler((webContents, permission /* 'media' */) => {
return false;
});
}
public async registerWebview(id: string, windowId: number, metadata: RegisterWebviewMetadata): Promise<void> {
@@ -93,7 +104,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
}
}
public async didLoadResource(requestId: number, content: VSBuffer | undefined): Promise<void> {
this.protocolProvider.didLoadResource(requestId, content);
public async didLoadResource(requestId: number, response: WebviewManagerDidLoadResourceResponse): Promise<void> {
this.protocolProvider.didLoadResource(requestId, response);
}
}

View File

@@ -5,14 +5,17 @@
import { protocol, session } from 'electron';
import { Readable } from 'stream';
import { bufferToStream, VSBuffer, VSBufferReadableStream } from 'vs/base/common/buffer';
import { bufferToStream, VSBufferReadableStream } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { FileAccess, Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRequestService } from 'vs/platform/request/common/request';
import { loadLocalResource, webviewPartitionId, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader';
import { loadLocalResource, readFileStream, WebviewFileReadResponse, webviewPartitionId, WebviewResourceFileReader, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader';
import { WebviewManagerDidLoadResourceResponse } from 'vs/platform/webview/common/webviewManagerService';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
interface WebviewMetadata {
@@ -34,12 +37,13 @@ export class WebviewProtocolProvider extends Disposable {
private readonly webviewMetadata = new Map<string, WebviewMetadata>();
private requestIdPool = 1;
private readonly pendingResourceReads = new Map<number, { resolve: (content: VSBuffer | undefined) => void }>();
private readonly pendingResourceReads = new Map<number, { resolve: (content: WebviewManagerDidLoadResourceResponse) => void }>();
constructor(
@IFileService private readonly fileService: IFileService,
@ILogService private readonly logService: ILogService,
@IRequestService private readonly requestService: IRequestService,
@IWindowsMainService readonly windowsMainService: IWindowsMainService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
) {
super();
@@ -125,7 +129,10 @@ export class WebviewProtocolProvider extends Disposable {
}
}
private async handleWebviewRequest(request: Electron.Request, callback: any) {
private async handleWebviewRequest(
request: Electron.ProtocolRequest,
callback: (response: string | Electron.ProtocolResponse) => void
) {
try {
const uri = URI.parse(request.url);
const entry = WebviewProtocolProvider.validWebviewFilePaths.get(uri.path);
@@ -144,11 +151,12 @@ export class WebviewProtocolProvider extends Disposable {
}
private async handleWebviewResourceRequest(
request: Electron.Request,
callback: (stream?: NodeJS.ReadableStream | Electron.StreamProtocolResponse | undefined) => void
request: Electron.ProtocolRequest,
callback: (stream: NodeJS.ReadableStream | Electron.ProtocolResponse) => void
) {
try {
const uri = URI.parse(request.url);
const ifNoneMatch = request.headers['If-None-Match'];
const id = uri.authority;
const metadata = this.webviewMetadata.get(id);
@@ -170,10 +178,10 @@ export class WebviewProtocolProvider extends Disposable {
};
}
const fileService = {
readFileStream: async (resource: URI): Promise<VSBufferReadableStream> => {
const fileReader: WebviewResourceFileReader = {
readFileStream: async (resource: URI, etag: string | undefined): Promise<WebviewFileReadResponse.Response> => {
if (resource.scheme === Schemas.file) {
return (await this.fileService.readFileStream(resource)).value;
return readFileStream(this.fileService, resource, etag);
}
// Unknown uri scheme. Try delegating the file read back to the renderer
@@ -185,57 +193,112 @@ export class WebviewProtocolProvider extends Disposable {
}
const requestId = this.requestIdPool++;
const p = new Promise<VSBuffer | undefined>(resolve => {
const p = new Promise<WebviewManagerDidLoadResourceResponse>(resolve => {
this.pendingResourceReads.set(requestId, { resolve });
});
window.send(`vscode:loadWebviewResource-${id}`, requestId, uri);
const result = await p;
if (!result) {
throw new FileOperationError('Could not read file', FileOperationResult.FILE_NOT_FOUND);
}
switch (result) {
case 'access-denied':
throw new FileOperationError('Could not read file', FileOperationResult.FILE_PERMISSION_DENIED);
return bufferToStream(result);
case 'not-found':
throw new FileOperationError('Could not read file', FileOperationResult.FILE_NOT_FOUND);
case 'not-modified':
return WebviewFileReadResponse.NotModified;
default:
return new WebviewFileReadResponse.StreamSuccess(bufferToStream(result.buffer), result.etag);
}
}
};
const result = await loadLocalResource(uri, {
const result = await loadLocalResource(uri, ifNoneMatch, {
extensionLocation: metadata.extensionLocation,
roots: metadata.localResourceRoots,
remoteConnectionData: metadata.remoteConnectionData,
rewriteUri,
}, fileService, this.requestService);
}, fileReader, this.requestService, this.logService, CancellationToken.None);
if (result.type === WebviewResourceResponse.Type.Success) {
return callback({
statusCode: 200,
data: this.streamToNodeReadable(result.stream),
headers: {
'Content-Type': result.mimeType,
'Access-Control-Allow-Origin': '*',
switch (result.type) {
case WebviewFileReadResponse.Type.Success:
{
const cacheHeaders: Record<string, string> = result.etag ? {
'ETag': result.etag,
'Cache-Control': 'no-cache'
} : {};
const ifNoneMatch = request.headers['If-None-Match'];
if (ifNoneMatch && result.etag === ifNoneMatch) {
/*
* Note that the server generating a 304 response MUST
* generate any of the following header fields that would
* have been sent in a 200 (OK) response to the same request:
* Cache-Control, Content-Location, Date, ETag, Expires, and Vary.
* (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match)
*/
return callback({
statusCode: 304, // not modified
data: undefined, // The request fails if `data` is not set
headers: {
'Content-Type': result.mimeType,
'Access-Control-Allow-Origin': '*',
...cacheHeaders
}
});
}
return callback({
statusCode: 200,
data: this.streamToNodeReadable(result.stream),
headers: {
'Content-Type': result.mimeType,
'Access-Control-Allow-Origin': '*',
...cacheHeaders
}
});
}
case WebviewResourceResponse.Type.NotModified:
{
/*
* Note that the server generating a 304 response MUST
* generate any of the following header fields that would
* have been sent in a 200 (OK) response to the same request:
* Cache-Control, Content-Location, Date, ETag, Expires, and Vary.
* (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match)
*/
return callback({
statusCode: 304, // not modified
data: undefined, // The request fails if `data` is not set
headers: {
'Content-Type': result.mimeType,
'Access-Control-Allow-Origin': '*',
}
});
}
case WebviewResourceResponse.Type.AccessDenied:
{
console.error('Webview: Cannot load resource outside of protocol root');
return callback({ data: undefined, statusCode: 401 });
}
});
}
if (result.type === WebviewResourceResponse.Type.AccessDenied) {
console.error('Webview: Cannot load resource outside of protocol root');
return callback({ data: null, statusCode: 401 });
}
}
} catch {
// noop
}
return callback({ data: null, statusCode: 404 });
return callback({ data: undefined, statusCode: 404 });
}
public didLoadResource(requestId: number, content: VSBuffer | undefined) {
public didLoadResource(requestId: number, response: WebviewManagerDidLoadResourceResponse) {
const pendingRead = this.pendingResourceReads.get(requestId);
if (!pendingRead) {
throw new Error('Unknown request');
}
this.pendingResourceReads.delete(requestId);
pendingRead.resolve(content);
pendingRead.resolve(response);
}
}