mirror of
https://github.com/coder/code-server.git
synced 2026-05-06 12:31:58 +02:00
chore(vscode): update to 1.54.2
This commit is contained in:
@@ -9,6 +9,8 @@ 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';
|
||||
@@ -18,7 +20,7 @@ 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;
|
||||
@@ -33,24 +35,77 @@ export namespace WebviewResourceResponse {
|
||||
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<{ stream: VSBufferReadableStream, etag?: string }>;
|
||||
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}`);
|
||||
|
||||
@@ -70,20 +125,45 @@ 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, undefined, mime);
|
||||
switch (response.res.statusCode) {
|
||||
case 200:
|
||||
return new WebviewResourceResponse.StreamSuccess(response.stream, response.res.headers['etag'], mime);
|
||||
|
||||
case 304: // Not modified
|
||||
return new WebviewResourceResponse.NotModified(mime);
|
||||
|
||||
default:
|
||||
return WebviewResourceResponse.Failed;
|
||||
}
|
||||
return WebviewResourceResponse.Failed;
|
||||
}
|
||||
|
||||
try {
|
||||
const contents = await fileReader.readFileStream(resourceToLoad);
|
||||
const contents = await fileReader.readFileStream(resourceToLoad, ifNoneMatch);
|
||||
logService.debug(`loadLocalResource - Loaded using fileReader. requestUri=${requestUri}`);
|
||||
|
||||
return new WebviewResourceResponse.StreamSuccess(contents.stream, contents.etag, mime);
|
||||
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);
|
||||
|
||||
@@ -19,6 +19,16 @@ export interface WebviewWindowId {
|
||||
readonly windowId: number;
|
||||
}
|
||||
|
||||
export type WebviewManagerDidLoadResourceResponse =
|
||||
VSBuffer
|
||||
| 'not-modified'
|
||||
| 'access-denied'
|
||||
| 'not-found';
|
||||
|
||||
export interface WebviewManagerDidLoadResourceResponseDetails {
|
||||
readonly etag?: string;
|
||||
}
|
||||
|
||||
export interface IWebviewManagerService {
|
||||
_serviceBrand: unknown;
|
||||
|
||||
@@ -26,7 +36,8 @@ export interface IWebviewManagerService {
|
||||
unregisterWebview(id: string): Promise<void>;
|
||||
updateWebviewMetadata(id: string, metadataDelta: Partial<RegisterWebviewMetadata>): Promise<void>;
|
||||
|
||||
didLoadResource(requestId: number, content: VSBuffer | undefined): void;
|
||||
/** Note: the VSBuffer must be a top level argument so that it can be serialized and deserialized properly */
|
||||
didLoadResource(requestId: number, response: WebviewManagerDidLoadResourceResponse, responseDetails?: WebviewManagerDidLoadResourceResponseDetails): void;
|
||||
|
||||
setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
* Licensed under the MIT License. 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, WebviewManagerDidLoadResourceResponseDetails, 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';
|
||||
@@ -33,6 +33,19 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
|
||||
super();
|
||||
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, callback) => {
|
||||
if (permission === 'clipboard-read') {
|
||||
return callback(true);
|
||||
}
|
||||
|
||||
return callback(false);
|
||||
});
|
||||
|
||||
sess.setPermissionCheckHandler((_webContents, permission /* 'media' */) => {
|
||||
return permission === 'clipboard-read';
|
||||
});
|
||||
}
|
||||
|
||||
public async registerWebview(id: string, windowId: number, metadata: RegisterWebviewMetadata): Promise<void> {
|
||||
@@ -78,7 +91,7 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
|
||||
if (typeof (id as WebviewWindowId).windowId === 'number') {
|
||||
const { windowId } = (id as WebviewWindowId);
|
||||
const window = this.windowsMainService.getWindowById(windowId);
|
||||
if (!window) {
|
||||
if (!window?.win) {
|
||||
throw new Error(`Invalid windowId: ${windowId}`);
|
||||
}
|
||||
contents = window.win.webContents;
|
||||
@@ -95,7 +108,11 @@ 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,
|
||||
responseDetails?: WebviewManagerDidLoadResourceResponseDetails,
|
||||
): Promise<void> {
|
||||
this.protocolProvider.didLoadResource(requestId, response, responseDetails);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OnBeforeRequestListenerDetails, session } from 'electron';
|
||||
import { OnBeforeRequestListenerDetails, session, webContents } from 'electron';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
|
||||
@@ -21,12 +22,14 @@ interface PortMappingData {
|
||||
readonly resolvedAuthority: IAddress | null | undefined;
|
||||
}
|
||||
|
||||
interface WebviewData {
|
||||
readonly manager: WebviewPortMappingManager;
|
||||
readonly metadata: PortMappingData;
|
||||
}
|
||||
|
||||
export class WebviewPortMappingProvider extends Disposable {
|
||||
|
||||
private readonly _webviewData = new Map<string, {
|
||||
readonly manager: WebviewPortMappingManager;
|
||||
metadata: PortMappingData;
|
||||
}>();
|
||||
private readonly _webviewData = new Map<string, WebviewData>();
|
||||
|
||||
constructor(
|
||||
@ITunnelService private readonly _tunnelService: ITunnelService,
|
||||
@@ -34,7 +37,6 @@ export class WebviewPortMappingProvider extends Disposable {
|
||||
super();
|
||||
|
||||
const sess = session.fromPartition(webviewPartitionId);
|
||||
|
||||
sess.webRequest.onBeforeRequest({
|
||||
urls: [
|
||||
'*://localhost:*/*',
|
||||
@@ -42,14 +44,26 @@ export class WebviewPortMappingProvider extends Disposable {
|
||||
'*://0.0.0.0:*/*',
|
||||
]
|
||||
}, async (details: OnBeforeRequestListenerDetails_Extended, callback) => {
|
||||
let origin: URI;
|
||||
let webviewId: string | undefined;
|
||||
try {
|
||||
origin = URI.parse(details.lastCommittedOrigin!);
|
||||
if (details.lastCommittedOrigin) {
|
||||
const origin = URI.parse(details.lastCommittedOrigin);
|
||||
webviewId = origin.authority;
|
||||
} else if (typeof details.webContentsId === 'number') {
|
||||
const contents = webContents.fromId(details.webContentsId);
|
||||
const url = URI.parse(contents.getURL());
|
||||
if (url.scheme === Schemas.vscodeWebview) {
|
||||
webviewId = url.authority;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return callback({});
|
||||
}
|
||||
|
||||
const webviewId = origin.authority;
|
||||
if (!webviewId) {
|
||||
return callback({});
|
||||
}
|
||||
|
||||
const entry = this._webviewData.get(webviewId);
|
||||
if (!entry) {
|
||||
return callback({});
|
||||
|
||||
@@ -5,15 +5,18 @@
|
||||
|
||||
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 { listenStream } from 'vs/base/common/stream';
|
||||
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, WebviewManagerDidLoadResourceResponseDetails } from 'vs/platform/webview/common/webviewManagerService';
|
||||
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
|
||||
|
||||
interface WebviewMetadata {
|
||||
@@ -23,6 +26,11 @@ interface WebviewMetadata {
|
||||
readonly remoteConnectionData: IRemoteConnectionData | null;
|
||||
}
|
||||
|
||||
interface PendingResourceResult {
|
||||
readonly response: WebviewManagerDidLoadResourceResponse;
|
||||
readonly responseDetails?: WebviewManagerDidLoadResourceResponseDetails;
|
||||
}
|
||||
|
||||
export class WebviewProtocolProvider extends Disposable {
|
||||
|
||||
private static validWebviewFilePaths = new Map([
|
||||
@@ -35,7 +43,7 @@ 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: PendingResourceResult) => void }>();
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@@ -73,28 +81,27 @@ export class WebviewProtocolProvider extends Disposable {
|
||||
if (!this.listening) {
|
||||
this.listening = true;
|
||||
|
||||
// Data
|
||||
stream.on('data', data => {
|
||||
try {
|
||||
if (!this.push(data.buffer)) {
|
||||
stream.pause(); // pause the stream if we should not push anymore
|
||||
listenStream(stream, {
|
||||
onData: data => {
|
||||
try {
|
||||
if (!this.push(data.buffer)) {
|
||||
stream.pause(); // pause the stream if we should not push anymore
|
||||
}
|
||||
} catch (error) {
|
||||
this.emit(error);
|
||||
}
|
||||
},
|
||||
onError: error => {
|
||||
this.emit('error', error);
|
||||
},
|
||||
onEnd: () => {
|
||||
try {
|
||||
this.push(null); // signal EOS
|
||||
} catch (error) {
|
||||
this.emit(error);
|
||||
}
|
||||
} catch (error) {
|
||||
this.emit(error);
|
||||
}
|
||||
});
|
||||
|
||||
// End
|
||||
stream.on('end', () => {
|
||||
try {
|
||||
this.push(null); // signal EOS
|
||||
} catch (error) {
|
||||
this.emit(error);
|
||||
}
|
||||
});
|
||||
|
||||
// Error
|
||||
stream.on('error', error => this.emit('error', error));
|
||||
}
|
||||
|
||||
// ensure the stream is flowing
|
||||
@@ -154,6 +161,7 @@ export class WebviewProtocolProvider extends Disposable {
|
||||
) {
|
||||
try {
|
||||
const uri = URI.parse(request.url);
|
||||
const ifNoneMatch = request.headers['If-None-Match'];
|
||||
|
||||
const id = uri.authority;
|
||||
const metadata = this.webviewMetadata.get(id);
|
||||
@@ -165,7 +173,11 @@ export class WebviewProtocolProvider extends Disposable {
|
||||
rewriteUri = (uri) => {
|
||||
if (metadata.remoteConnectionData) {
|
||||
if (uri.scheme === Schemas.vscodeRemote || (metadata.extensionLocation?.scheme === Schemas.vscodeRemote)) {
|
||||
return URI.parse(`http://${metadata.remoteConnectionData.host}:${metadata.remoteConnectionData.port}`).with({
|
||||
let host = metadata.remoteConnectionData.host;
|
||||
if (host && host.indexOf(':') !== -1) { // IPv6 address
|
||||
host = `[${host}]`;
|
||||
}
|
||||
return URI.parse(`http://${host}:${metadata.remoteConnectionData.port}`).with({
|
||||
path: '/vscode-remote-resource',
|
||||
query: `tkn=${metadata.remoteConnectionData.connectionToken}&path=${encodeURIComponent(uri.path)}`,
|
||||
});
|
||||
@@ -175,14 +187,10 @@ export class WebviewProtocolProvider extends Disposable {
|
||||
};
|
||||
}
|
||||
|
||||
const fileReader = {
|
||||
readFileStream: async (resource: URI): Promise<{ stream: VSBufferReadableStream, etag?: string }> => {
|
||||
const fileReader: WebviewResourceFileReader = {
|
||||
readFileStream: async (resource: URI, etag: string | undefined): Promise<WebviewFileReadResponse.Response> => {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
const result = (await this.fileService.readFileStream(resource));
|
||||
return {
|
||||
stream: result.value,
|
||||
etag: result.etag
|
||||
};
|
||||
return readFileStream(this.fileService, resource, etag);
|
||||
}
|
||||
|
||||
// Unknown uri scheme. Try delegating the file read back to the renderer
|
||||
@@ -194,68 +202,97 @@ export class WebviewProtocolProvider extends Disposable {
|
||||
}
|
||||
|
||||
const requestId = this.requestIdPool++;
|
||||
const p = new Promise<VSBuffer | undefined>(resolve => {
|
||||
const p = new Promise<PendingResourceResult>(resolve => {
|
||||
this.pendingResourceReads.set(requestId, { resolve });
|
||||
});
|
||||
|
||||
window.send(`vscode:loadWebviewResource-${id}`, requestId, uri);
|
||||
window.send(`vscode:loadWebviewResource-${id}`, requestId, uri, etag);
|
||||
|
||||
const result = await p;
|
||||
if (!result) {
|
||||
throw new FileOperationError('Could not read file', FileOperationResult.FILE_NOT_FOUND);
|
||||
}
|
||||
switch (result.response) {
|
||||
case 'access-denied':
|
||||
throw new FileOperationError('Could not read file', FileOperationResult.FILE_PERMISSION_DENIED);
|
||||
|
||||
return { stream: bufferToStream(result), etag: undefined };
|
||||
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.response), result.responseDetails?.etag);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const result = await loadLocalResource(uri, {
|
||||
const result = await loadLocalResource(uri, ifNoneMatch, {
|
||||
extensionLocation: metadata.extensionLocation,
|
||||
roots: metadata.localResourceRoots,
|
||||
remoteConnectionData: metadata.remoteConnectionData,
|
||||
rewriteUri,
|
||||
}, fileReader, this.requestService, this.logService);
|
||||
}, fileReader, this.requestService, this.logService, CancellationToken.None);
|
||||
|
||||
if (result.type === WebviewResourceResponse.Type.Success) {
|
||||
const cacheHeaders: Record<string, string> = result.etag ? {
|
||||
'ETag': result.etag,
|
||||
'Cache-Control': 'no-cache'
|
||||
} : {};
|
||||
switch (result.type) {
|
||||
case WebviewResourceResponse.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
|
||||
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
|
||||
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: undefined, statusCode: 401 });
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -265,12 +302,16 @@ export class WebviewProtocolProvider extends Disposable {
|
||||
return callback({ data: undefined, statusCode: 404 });
|
||||
}
|
||||
|
||||
public didLoadResource(requestId: number, content: VSBuffer | undefined) {
|
||||
public didLoadResource(
|
||||
requestId: number,
|
||||
response: WebviewManagerDidLoadResourceResponse,
|
||||
responseDetails?: WebviewManagerDidLoadResourceResponseDetails,
|
||||
) {
|
||||
const pendingRead = this.pendingResourceReads.get(requestId);
|
||||
if (!pendingRead) {
|
||||
throw new Error('Unknown request');
|
||||
}
|
||||
this.pendingResourceReads.delete(requestId);
|
||||
pendingRead.resolve(content);
|
||||
pendingRead.resolve({ response, responseDetails });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user