Move html rewriting for old webviews to (#163367)

The `asWebviewUri` methods was introduced in VS Code 1.38. It's silly that we still force every single webview to pay the cost of trying to rewrite the old style uris we supported in very old versions of VS Code

Instead I've moved this logic into the extension host and disabled it for all extensions that target VS Code 1.60+ or newer. This means it never applies to internal webviews, notebooks, webview views, or custom editors (these public apis were all introduced after the switch to `asWebviewUri`)
This commit is contained in:
Matt Bierner 2022-10-11 16:08:17 -07:00 committed by GitHub
parent 0abb1e99c2
commit d05d85a78b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 32 deletions

View file

@ -8,7 +8,7 @@
"license": "MIT",
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
"engines": {
"vscode": "^1.20.0"
"vscode": "^1.70.0"
},
"main": "./out/extension",
"browser": "./dist/browser/extension",

View file

@ -12,7 +12,7 @@
"license": "MIT",
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
"engines": {
"vscode": "^1.39.0"
"vscode": "^1.70.0"
},
"main": "./out/extension",
"browser": "./dist/browser/extension.js",

View file

@ -11,7 +11,7 @@
"license": "MIT",
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
"engines": {
"vscode": "^1.53.0"
"vscode": "^1.70.0"
},
"main": "./out/extension",
"browser": "./dist/browser/extension",

View file

@ -33,7 +33,8 @@ export class ExtHostWebview implements vscode.Webview {
#isDisposed: boolean = false;
#hasCalledAsWebviewUri = false;
#serializeBuffersForPostMessage = false;
#serializeBuffersForPostMessage: boolean;
#shouldRewriteOldResourceUris: boolean;
constructor(
handle: extHostProtocol.WebviewHandle,
@ -51,6 +52,7 @@ export class ExtHostWebview implements vscode.Webview {
this.#workspace = workspace;
this.#extension = extension;
this.#serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
this.#shouldRewriteOldResourceUris = shouldTryRewritingOldResourceUris(extension);
this.#deprecationService = deprecationService;
}
@ -98,12 +100,12 @@ export class ExtHostWebview implements vscode.Webview {
this.assertNotDisposed();
if (this.#html !== value) {
this.#html = value;
if (!this.#hasCalledAsWebviewUri && /(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) {
if (this.#shouldRewriteOldResourceUris && !this.#hasCalledAsWebviewUri && /(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) {
this.#hasCalledAsWebviewUri = true;
this.#deprecationService.report('Webview vscode-resource: uris', this.#extension,
`Please migrate to use the 'webview.asWebviewUri' api instead: https://aka.ms/vscode-webview-use-aswebviewuri`);
}
this.#proxy.$setHtml(this.#handle, value);
this.#proxy.$setHtml(this.#handle, this.rewriteOldResourceUrlsIfNeeded(value));
}
}
@ -131,6 +133,32 @@ export class ExtHostWebview implements vscode.Webview {
throw new Error('Webview is disposed');
}
}
private rewriteOldResourceUrlsIfNeeded(value: string): string {
if (!this.#shouldRewriteOldResourceUris) {
return value;
}
const isRemote = this.#extension.extensionLocation?.scheme === Schemas.vscodeRemote;
const remoteAuthority = this.#extension.extensionLocation.scheme === Schemas.vscodeRemote ? this.#extension.extensionLocation.authority : undefined;
return value
.replace(/(["'])(?:vscode-resource):(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => {
const uri = URI.from({
scheme: scheme || 'file',
path: decodeURIComponent(path),
});
const webviewUri = asWebviewUri(uri, { isRemote, authority: remoteAuthority }).toString();
return `${startQuote}${webviewUri}${endQuote}`;
})
.replace(/(["'])(?:vscode-webview-resource):(\/\/[^\s\/'"]+\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => {
const uri = URI.from({
scheme: scheme || 'file',
path: decodeURIComponent(path),
});
const webviewUri = asWebviewUri(uri, { isRemote, authority: remoteAuthority }).toString();
return `${startQuote}${webviewUri}${endQuote}`;
});
}
}
export function shouldSerializeBuffersForPostMessage(extension: IExtensionDescription): boolean {
@ -142,6 +170,19 @@ export function shouldSerializeBuffersForPostMessage(extension: IExtensionDescri
}
}
function shouldTryRewritingOldResourceUris(extension: IExtensionDescription): boolean {
try {
const version = normalizeVersion(parseVersion(extension.engines.vscode));
if (!version) {
return false;
}
return version.majorBase < 1 || (version.majorBase === 1 && version.minorBase < 60);
} catch {
return false;
}
}
export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape;

View file

@ -11,7 +11,7 @@ import { streamToBuffer, VSBufferReadableStream } from 'vs/base/common/buffer';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { COI, Schemas } from 'vs/base/common/network';
import { COI } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { localize } from 'vs/nls';
@ -30,7 +30,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ITunnelService } from 'vs/platform/tunnel/common/tunnel';
import { WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping';
import { parentOriginHash } from 'vs/workbench/browser/webview';
import { asWebviewUri, decodeAuthority, webviewGenericCspSource, webviewRootResourceAuthority } from 'vs/workbench/common/webview';
import { decodeAuthority, webviewGenericCspSource, webviewRootResourceAuthority } from 'vs/workbench/common/webview';
import { loadLocalResource, WebviewResourceResponse } from 'vs/workbench/contrib/webview/browser/resourceLoading';
import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing';
import { areWebviewContentOptionsEqual, IWebview, WebviewContentOptions, WebviewExtensionDescription, WebviewMessageReceivedEvent, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
@ -645,37 +645,14 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
}
public set html(value: string) {
const rewrittenHtml = this.rewriteVsCodeResourceUrls(value);
this.doUpdateContent({
html: rewrittenHtml,
html: value,
options: this.content.options,
state: this.content.state,
});
this._onDidHtmlChange.fire(value);
}
private rewriteVsCodeResourceUrls(value: string): string {
const isRemote = this.extension?.location?.scheme === Schemas.vscodeRemote;
const remoteAuthority = this.extension?.location?.scheme === Schemas.vscodeRemote ? this.extension.location.authority : undefined;
return value
.replace(/(["'])(?:vscode-resource):(\/\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => {
const uri = URI.from({
scheme: scheme || 'file',
path: decodeURIComponent(path),
});
const webviewUri = asWebviewUri(uri, { isRemote, authority: remoteAuthority }).toString();
return `${startQuote}${webviewUri}${endQuote}`;
})
.replace(/(["'])(?:vscode-webview-resource):(\/\/[^\s\/'"]+\/([^\s\/'"]+?)(?=\/))?([^\s'"]+?)(["'])/gi, (_match, startQuote, _1, scheme, path, endQuote) => {
const uri = URI.from({
scheme: scheme || 'file',
path: decodeURIComponent(path),
});
const webviewUri = asWebviewUri(uri, { isRemote, authority: remoteAuthority }).toString();
return `${startQuote}${webviewUri}${endQuote}`;
});
}
public set contentOptions(options: WebviewContentOptions) {
this._logService.debug(`Webview(${this.id}): will update content options`);