From d05d85a78b3790bab72d205dcb7a684a18309540 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 11 Oct 2022 16:08:17 -0700 Subject: [PATCH] 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`) --- .../markdown-language-features/package.json | 2 +- extensions/media-preview/package.json | 2 +- extensions/simple-browser/package.json | 2 +- src/vs/workbench/api/common/extHostWebview.ts | 47 +++++++++++++++++-- .../contrib/webview/browser/webviewElement.ts | 29 ++---------- 5 files changed, 50 insertions(+), 32 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 763f946d9a3..2d8d4487f17 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -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", diff --git a/extensions/media-preview/package.json b/extensions/media-preview/package.json index b82a4eef836..384ab75f58b 100644 --- a/extensions/media-preview/package.json +++ b/extensions/media-preview/package.json @@ -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", diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index 2db456cb387..bcd83d483d3 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -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", diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index db22cb7fc13..11359a68969 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -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; diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index bb38d8b1756..45e735a28ed 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -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`);