Finalize asWebviewUri api

Fixes #79242

As discussed, renames `toWebviewResource` to `asWebviewUri` to be consistent with the `asAbsolutePath` api naming
This commit is contained in:
Matt Bierner 2019-08-19 20:18:26 -07:00
parent 7af2ebb9fc
commit c5dc9d16be
10 changed files with 59 additions and 66 deletions

View file

@ -439,8 +439,8 @@ export class MarkdownPreview extends Disposable {
if (this._resource === markdownResource) {
const self = this;
const resourceProvider: WebviewResourceProvider = {
toWebviewResource: (resource) => {
return this.editor.webview.toWebviewResource(normalizeResource(markdownResource, resource));
asWebviewUri: (resource) => {
return this.editor.webview.asWebviewUri(normalizeResource(markdownResource, resource));
},
get cspSource() { return self.editor.webview.cspSource; }
};

View file

@ -65,7 +65,7 @@ export class MarkdownContentProvider {
scrollEditorWithPreview: config.scrollEditorWithPreview,
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor,
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings(),
webviewResourceRoot: resourceProvider.toWebviewResource(markdownDocument.uri).toString(),
webviewResourceRoot: resourceProvider.asWebviewUri(markdownDocument.uri).toString(),
};
this.logger.log('provideTextDocumentContent', initialData);
@ -86,7 +86,7 @@ export class MarkdownContentProvider {
data-state="${escapeAttribute(JSON.stringify(state || {}))}">
<script src="${this.extensionResourcePath(resourceProvider, 'pre.js')}" nonce="${nonce}"></script>
${this.getStyles(resourceProvider, sourceUri, config, state)}
<base href="${resourceProvider.toWebviewResource(markdownDocument.uri)}">
<base href="${resourceProvider.asWebviewUri(markdownDocument.uri)}">
</head>
<body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}">
${body}
@ -110,7 +110,7 @@ export class MarkdownContentProvider {
}
private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string {
const webviewResource = resourceProvider.toWebviewResource(
const webviewResource = resourceProvider.asWebviewUri(
vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))));
return webviewResource.toString();
}
@ -126,17 +126,17 @@ export class MarkdownContentProvider {
// Assume it must be a local file
if (path.isAbsolute(href)) {
return resourceProvider.toWebviewResource(vscode.Uri.file(href)).toString();
return resourceProvider.asWebviewUri(vscode.Uri.file(href)).toString();
}
// Use a workspace relative path if there is a workspace
const root = vscode.workspace.getWorkspaceFolder(resource);
if (root) {
return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString();
return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString();
}
// Otherwise look relative to the markdown file
return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString();
return resourceProvider.asWebviewUri(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString();
}
private computeCustomStyleSheetIncludes(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
@ -176,7 +176,7 @@ export class MarkdownContentProvider {
private getStyles(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration, state?: any): string {
const baseStyles: string[] = [];
for (const resource of this.contributionProvider.contributions.previewStyles) {
baseStyles.push(`<link rel="stylesheet" type="text/css" href="${escapeAttribute(resourceProvider.toWebviewResource(resource))}">`);
baseStyles.push(`<link rel="stylesheet" type="text/css" href="${escapeAttribute(resourceProvider.asWebviewUri(resource))}">`);
}
return `${baseStyles.join('\n')}
@ -188,7 +188,7 @@ export class MarkdownContentProvider {
const out: string[] = [];
for (const resource of this.contributionProvider.contributions.previewScripts) {
out.push(`<script async
src="${escapeAttribute(resourceProvider.toWebviewResource(resource))}"
src="${escapeAttribute(resourceProvider.asWebviewUri(resource))}"
nonce="${nonce}"
charset="UTF-8"></script>`);
}

View file

@ -6,7 +6,7 @@
import * as vscode from 'vscode';
export interface WebviewResourceProvider {
toWebviewResource(resource: vscode.Uri): vscode.Uri;
asWebviewUri(resource: vscode.Uri): vscode.Uri;
readonly cspSource: string;
}
@ -30,4 +30,4 @@ export function normalizeResource(
}
}
return resource;
}
}

View file

@ -251,18 +251,18 @@ suite('Webview tests', () => {
});
</script>`);
async function toWebviewResource(path: string) {
const root = await webview.webview.toWebviewResource(vscode.Uri.file(vscode.workspace.rootPath!));
async function asWebviewUri(path: string) {
const root = await webview.webview.asWebviewUri(vscode.Uri.file(vscode.workspace.rootPath!));
return root.toString() + path;
}
{
const imagePath = await toWebviewResource('/image.png');
const imagePath = await asWebviewUri('/image.png');
const response = sendRecieveMessage(webview, { src: imagePath });
assert.strictEqual((await response).value, true);
}
{
const imagePath = await toWebviewResource('/no-such-image.png');
const imagePath = await asWebviewUri('/no-such-image.png');
const response = sendRecieveMessage(webview, { src: imagePath });
assert.strictEqual((await response).value, false);
}

24
src/vs/vscode.d.ts vendored
View file

@ -5923,6 +5923,30 @@ declare module 'vscode' {
* @param message Body of the message.
*/
postMessage(message: any): Thenable<boolean>;
/**
* Convert a uri for the local file system to one that can be used inside webviews.
*
* Webviews cannot directly load resoruces from the workspace or local file system using `file:` uris. The
* `asWebviewUri` function takes a local `file:` uri and converts it into a uri that can be used inside of
* a webview to load the same resource:
*
* ```ts
* webview.html = `<img src="${webview.asWebviewUri(vscode.Uri.file('/Users/codey/workspace/cat.gif'))}">`
* ```
*/
asWebviewUri(localResource: Uri): Uri;
/**
* Content security policy source for webview resources.
*
* This is the origin that should be used in a content security policy rule:
*
* ```
* img-src https: ${webview.cspSource} ...;
* ```
*/
readonly cspSource: string;
}
/**

View file

@ -1141,35 +1141,4 @@ declare module 'vscode' {
}
//#endregion
//#region Webview Resource Roots
export interface Webview {
/**
* Convert a uri for the local file system to one that can be used inside webviews.
*
* Webviews cannot directly load resoruces from the workspace or local file system using `file:` uris. The
* `toWebviewResource` function takes a local `file:` uri and converts it into a uri that can be used inside of
* a webview to load the same resource:
*
* ```ts
* webview.html = `<img src="${webview.toWebviewResource(vscode.Uri.file('/Users/codey/workspace/cat.gif'))}">`
* ```
*/
toWebviewResource(localResource: Uri): Uri;
/**
* Content security policy source for webview resources.
*
* This is the origin that should be used in a content security policy rule:
*
* ```
* img-src https: ${webview.cspSource} ...;
* ```
*/
readonly cspSource: string;
}
//#endregion
}

View file

@ -10,7 +10,7 @@ import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor';
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
import * as vscode from 'vscode';
import { ExtHostEditorInsetsShape, MainThreadEditorInsetsShape } from './extHost.protocol';
import { toWebviewResource, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import { generateUuid } from 'vs/base/common/uuid';
export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
@ -65,8 +65,8 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
private _html: string = '';
private _options: vscode.WebviewOptions = Object.create(null);
toWebviewResource(resource: vscode.Uri): vscode.Uri {
return toWebviewResource(that._initData, this._uuid, resource);
asWebviewUri(resource: vscode.Uri): vscode.Uri {
return asWebviewUri(that._initData, this._uuid, resource);
}
get cspSource(): string {

View file

@ -12,7 +12,7 @@ import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShap
import { Disposable } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as modes from 'vs/editor/common/modes';
import { WebviewInitData, toWebviewResource } from 'vs/workbench/api/common/shared/webview';
import { WebviewInitData, asWebviewUri } from 'vs/workbench/api/common/shared/webview';
import { generateUuid } from 'vs/base/common/uuid';
type IconPath = URI | { light: URI, dark: URI };
@ -35,8 +35,8 @@ export class ExtHostWebview implements vscode.Webview {
this._onMessageEmitter.dispose();
}
public toWebviewResource(resource: vscode.Uri): vscode.Uri {
return toWebviewResource(this._initData, this._handle, resource);
public asWebviewUri(resource: vscode.Uri): vscode.Uri {
return asWebviewUri(this._initData, this._handle, resource);
}
public get cspSource(): string {

View file

@ -11,7 +11,7 @@ export interface WebviewInitData {
readonly webviewCspSource: string;
}
export function toWebviewResource(
export function asWebviewUri(
initData: WebviewInitData,
uuid: string,
resource: vscode.Uri

View file

@ -48,7 +48,7 @@ suite('ExtHostWebview', () => {
assert.strictEqual(lastInvokedDeserializer, serializerB);
});
test('toWebviewResource for desktop vscode-resource scheme', () => {
test('asWebviewUri for desktop vscode-resource scheme', () => {
const shape = createNoopMainThreadWebviews();
const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), {
webviewCspSource: '',
@ -57,37 +57,37 @@ suite('ExtHostWebview', () => {
const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {});
assert.strictEqual(
webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html')).toString(),
webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString(),
'vscode-resource:/Users/codey/file.html',
'Unix basic'
);
assert.strictEqual(
webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html#frag')).toString(),
webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString(),
'vscode-resource:/Users/codey/file.html#frag',
'Unix should preserve fragment'
);
assert.strictEqual(
webview.webview.toWebviewResource(URI.parse('file:///Users/codey/f%20ile.html')).toString(),
webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString(),
'vscode-resource:/Users/codey/f%20ile.html',
'Unix with encoding'
);
assert.strictEqual(
webview.webview.toWebviewResource(URI.parse('file://localhost/Users/codey/file.html')).toString(),
webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString(),
'vscode-resource://localhost/Users/codey/file.html',
'Unix should preserve authority'
);
assert.strictEqual(
webview.webview.toWebviewResource(URI.parse('file:///c:/codey/file.txt')).toString(),
webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString(),
'vscode-resource:/c%3A/codey/file.txt',
'Windows C drive'
);
});
test('toWebviewResource for web endpoint', () => {
test('asWebviewUri for web endpoint', () => {
const shape = createNoopMainThreadWebviews();
const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), {
@ -101,31 +101,31 @@ suite('ExtHostWebview', () => {
}
assert.strictEqual(
stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html')).toString()),
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString()),
'webview.contoso.com/commit///Users/codey/file.html',
'Unix basic'
);
assert.strictEqual(
stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/file.html#frag')).toString()),
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString()),
'webview.contoso.com/commit///Users/codey/file.html#frag',
'Unix should preserve fragment'
);
assert.strictEqual(
stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///Users/codey/f%20ile.html')).toString()),
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString()),
'webview.contoso.com/commit///Users/codey/f%20ile.html',
'Unix with encoding'
);
assert.strictEqual(
stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file://localhost/Users/codey/file.html')).toString()),
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString()),
'webview.contoso.com/commit//localhost/Users/codey/file.html',
'Unix should preserve authority'
);
assert.strictEqual(
stripEndpointUuid(webview.webview.toWebviewResource(URI.parse('file:///c:/codey/file.txt')).toString()),
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString()),
'webview.contoso.com/commit///c%3A/codey/file.txt',
'Windows C drive'
);