提交 8119b4ae 编写于 作者: M Matt Bierner

Move the webviewResourceRoot property to be set on each webview instead of as a global property

For #72155

This allows  us to potentially change the resource root per webview
上级 10831237
......@@ -412,7 +412,7 @@ export class MarkdownPreview extends Disposable {
this.currentVersion = pendingVersion;
if (this._resource === resource) {
const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state);
const content = await this._contentProvider.provideTextDocumentContent(document, await this.editor.webview.resourceRoot, this._previewConfigurations, this.line, this.state);
// Another call to `doUpdate` may have happened.
// Make sure we are still updating for the correct document
if (this.currentVersion && this.currentVersion.equals(pendingVersion)) {
......
......@@ -51,6 +51,7 @@ export class MarkdownContentProvider {
public async provideTextDocumentContent(
markdownDocument: vscode.TextDocument,
webviewResourceRoot: string,
previewConfigurations: MarkdownPreviewConfigurationManager,
initialLine: number | undefined = undefined,
state?: any
......@@ -65,14 +66,14 @@ export class MarkdownContentProvider {
scrollEditorWithPreview: config.scrollEditorWithPreview,
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor,
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings(),
webviewResourceRoot: vscode.env.webviewResourceRoot,
webviewResourceRoot: webviewResourceRoot,
};
this.logger.log('provideTextDocumentContent', initialData);
// Content Security Policy
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
const csp = this.getCspForResource(sourceUri, nonce);
const csp = this.getCspForResource(webviewResourceRoot, sourceUri, nonce);
const body = await this.engine.render(markdownDocument);
return `<!DOCTYPE html>
......@@ -84,14 +85,14 @@ export class MarkdownContentProvider {
data-settings="${escapeAttribute(JSON.stringify(initialData))}"
data-strings="${escapeAttribute(JSON.stringify(previewStrings))}"
data-state="${escapeAttribute(JSON.stringify(state || {}))}">
<script src="${this.extensionResourcePath('pre.js')}" nonce="${nonce}"></script>
${this.getStyles(sourceUri, nonce, config, state)}
<base href="${toResoruceUri(markdownDocument.uri)}">
<script src="${this.extensionResourcePath(webviewResourceRoot, 'pre.js')}" nonce="${nonce}"></script>
${this.getStyles(webviewResourceRoot, sourceUri, nonce, config, state)}
<base href="${toResoruceUri(webviewResourceRoot, markdownDocument.uri)}">
</head>
<body class="vscode-body ${config.scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${config.wordWrap ? 'wordWrap' : ''} ${config.markEditorSelection ? 'showEditorSelection' : ''}">
${body}
<div class="code-line" data-line="${markdownDocument.lineCount}"></div>
${this.getScripts(nonce)}
${this.getScripts(webviewResourceRoot, nonce)}
</body>
</html>`;
}
......@@ -109,12 +110,12 @@ export class MarkdownContentProvider {
</html>`;
}
private extensionResourcePath(mediaFile: string): string {
return toResoruceUri(vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))))
private extensionResourcePath(webviewResourceRoot: string, mediaFile: string): string {
return toResoruceUri(webviewResourceRoot, vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))))
.toString();
}
private fixHref(resource: vscode.Uri, href: string): string {
private fixHref(webviewResourceRoot: string, resource: vscode.Uri, href: string): string {
if (!href) {
return href;
}
......@@ -125,23 +126,23 @@ export class MarkdownContentProvider {
// Assume it must be a local file
if (path.isAbsolute(href)) {
return toResoruceUri(vscode.Uri.file(href)).toString();
return toResoruceUri(webviewResourceRoot, vscode.Uri.file(href)).toString();
}
// Use a workspace relative path if there is a workspace
const root = vscode.workspace.getWorkspaceFolder(resource);
if (root) {
return toResoruceUri(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString();
return toResoruceUri(webviewResourceRoot, vscode.Uri.file(path.join(root.uri.fsPath, href))).toString();
}
// Otherwise look relative to the markdown file
return toResoruceUri(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString();
return toResoruceUri(webviewResourceRoot, vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString();
}
private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
private computeCustomStyleSheetIncludes(webviewResourceRoot: string, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
if (Array.isArray(config.styles)) {
return config.styles.map(style => {
return `<link rel="stylesheet" class="code-user-style" data-source="${escapeAttribute(style)}" href="${escapeAttribute(this.fixHref(resource, style))}" type="text/css" media="screen">`;
return `<link rel="stylesheet" class="code-user-style" data-source="${escapeAttribute(style)}" href="${escapeAttribute(this.fixHref(webviewResourceRoot, resource, style))}" type="text/css" media="screen">`;
}).join('\n');
}
return '';
......@@ -172,37 +173,41 @@ export class MarkdownContentProvider {
return ret;
}
private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string {
private getStyles(webviewResourceRoot: string, resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string {
const baseStyles = this.contributionProvider.contributions.previewStyles
.map(resource => `<link rel="stylesheet" type="text/css" href="${escapeAttribute(resource.toString())}">`)
.map(resource => `<link rel="stylesheet" type="text/css" href="${escapeAttribute(toResoruceUri(webviewResourceRoot, resource).toString())}">`)
.join('\n');
return `${baseStyles}
${this.getSettingsOverrideStyles(nonce, config)}
${this.computeCustomStyleSheetIncludes(resource, config)}
${this.computeCustomStyleSheetIncludes(webviewResourceRoot, resource, config)}
${this.getImageStabilizerStyles(state)}`;
}
private getScripts(nonce: string): string {
private getScripts(resourceRoot: string, nonce: string): string {
return this.contributionProvider.contributions.previewScripts
.map(resource => `<script async src="${escapeAttribute(resource.toString())}" nonce="${nonce}" charset="UTF-8"></script>`)
.map(resource => `<script async src="${escapeAttribute(toResoruceUri(resourceRoot, resource).toString())}" nonce="${nonce}" charset="UTF-8"></script>`)
.join('\n');
}
private getCspForResource(resource: vscode.Uri, nonce: string): string {
private getCspForResource(
webviewResourceRoot: string,
resource: vscode.Uri,
nonce: string
): string {
switch (this.cspArbiter.getSecurityLevelForResource(resource)) {
case MarkdownPreviewSecurityLevel.AllowInsecureContent:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${vscode.env.webviewResourceRoot} http: https: data:; media-src 'self' ${vscode.env.webviewResourceRoot} http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${vscode.env.webviewResourceRoot} 'unsafe-inline' http: https: data:; font-src 'self' ${vscode.env.webviewResourceRoot} http: https: data:;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${webviewResourceRoot} http: https: data:; media-src 'self' ${webviewResourceRoot} http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${webviewResourceRoot} 'unsafe-inline' http: https: data:; font-src 'self' ${webviewResourceRoot} http: https: data:;">`;
case MarkdownPreviewSecurityLevel.AllowInsecureLocalContent:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${vscode.env.webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*; media-src 'self' ${vscode.env.webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src 'self' ${vscode.env.webviewResourceRoot} 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src 'self' ${vscode.env.webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*; media-src 'self' ${webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src 'self' ${webviewResourceRoot} 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src 'self' ${webviewResourceRoot} https: data: http://localhost:* http://127.0.0.1:*;">`;
case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
return '';
case MarkdownPreviewSecurityLevel.Strict:
default:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${vscode.env.webviewResourceRoot} https: data:; media-src 'self' ${vscode.env.webviewResourceRoot} https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${vscode.env.webviewResourceRoot} 'unsafe-inline' https: data:; font-src 'self' ${vscode.env.webviewResourceRoot} https: data:;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${webviewResourceRoot} https: data:; media-src 'self' ${webviewResourceRoot} https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${webviewResourceRoot} 'unsafe-inline' https: data:; font-src 'self' ${webviewResourceRoot} https: data:;">`;
}
}
}
......@@ -7,10 +7,9 @@ import * as vscode from 'vscode';
import * as path from 'path';
import { Disposable } from './util/dispose';
import * as arrays from './util/arrays';
import { toResoruceUri } from './util/resources';
const resolveExtensionResource = (extension: vscode.Extension<any>, resourcePath: string): vscode.Uri => {
return toResoruceUri(vscode.Uri.file(path.join(extension.extensionPath, resourcePath)));
return vscode.Uri.file(path.join(extension.extensionPath, resourcePath));
};
const resolveExtensionResources = (extension: vscode.Extension<any>, resourcePaths: unknown): vscode.Uri[] => {
......
......@@ -5,9 +5,9 @@
import * as vscode from 'vscode';
const rootUri = vscode.Uri.parse(vscode.env.webviewResourceRoot);
export function toResoruceUri(uri: vscode.Uri): vscode.Uri {
export function toResoruceUri(webviewResourceRoot: string, uri: vscode.Uri): vscode.Uri {
const rootUri = vscode.Uri.parse(webviewResourceRoot);
return rootUri.with({
path: rootUri.path + uri.path,
query: uri.query,
......
......@@ -251,7 +251,7 @@ suite('Webview tests', () => {
});
</script>`);
const workspaceRootUri = vscode.env.webviewResourceRoot + vscode.Uri.file(vscode.workspace.rootPath!).path;
const workspaceRootUri = webview.webview.resourceRoot + vscode.Uri.file(vscode.workspace.rootPath!).path;
{
const imagePath = workspaceRootUri.toString() + '/image.png';
......
......@@ -1461,14 +1461,14 @@ declare module 'vscode' {
//#region Webview Resource Roots
export namespace env {
export interface Webview {
/**
* Root url from which local resources are loaded inside of webviews.
*
* This is `vscode-resource:` when vscode is run on the desktop. When vscode is run
* on the web, this points to a server endpoint.
*/
export const webviewResourceRoot: string;
readonly resourceRoot: Thenable<string>;
}
//#endregion
......
......@@ -12,6 +12,7 @@ import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/we
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
// todo@joh move these things back into something like contrib/insets
class EditorWebviewZone implements IViewZone {
......@@ -59,6 +60,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
constructor(
context: IExtHostContext,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@IWebviewService private readonly _webviewService: IWebviewService,
) {
......@@ -144,4 +146,8 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
}
return Promise.resolve(false);
}
async $getResourceRoot(_handle: number): Promise<string> {
return this._environmentService.webviewResourceRoot;
}
}
......@@ -23,6 +23,7 @@ import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/commo
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { IProductService } from 'vs/platform/product/common/product';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape {
......@@ -54,6 +55,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
@IOpenerService private readonly _openerService: IOpenerService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IProductService private readonly _productService: IProductService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
) {
super();
......@@ -139,6 +141,10 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
webview.setOptions(reviveWebviewOptions(options as any /*todo@mat */));
}
async $getResourceRoot(_handle: WebviewPanelHandle): Promise<string> {
return this._environmentService.webviewResourceRoot;
}
public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void {
const webview = this.getWebview(handle);
if (webview.isDisposed()) {
......
......@@ -59,7 +59,6 @@ export interface IEnvironment {
extensionTestsLocationURI?: URI;
globalStorageHome: URI;
userHome: URI;
webviewResourceRoot: string;
}
export interface IStaticWorkspaceData {
......@@ -519,6 +518,7 @@ export interface MainThreadEditorInsetsShape extends IDisposable {
$setHtml(handle: number, value: string): void;
$setOptions(handle: number, options: modes.IWebviewOptions): void;
$postMessage(handle: number, value: any): Promise<boolean>;
$getResourceRoot(handle: number): Promise<string>;
}
export interface ExtHostEditorInsetsShape {
......@@ -543,6 +543,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
$setHtml(handle: WebviewPanelHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void;
$postMessage(handle: WebviewPanelHandle, value: any): Promise<boolean>;
$getResourceRoot(handle: WebviewPanelHandle): Promise<string>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
......
......@@ -61,6 +61,10 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
private _html: string = '';
private _options: vscode.WebviewOptions;
get resourceRoot(): Promise<string> {
return that._proxy.$getResourceRoot(handle);
}
set options(value: vscode.WebviewOptions) {
this._options = value;
that._proxy.$setOptions(handle, value);
......
......@@ -39,6 +39,10 @@ export class ExtHostWebview implements vscode.Webview {
this._onMessageEmitter.dispose();
}
public get resourceRoot(): Promise<string> {
return this._proxy.$getResourceRoot(this._handle);
}
public get html(): string {
this.assertNotDisposed();
return this._html;
......
......@@ -262,10 +262,6 @@ export function createApiFactory(
openExternal(uri: URI) {
return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote });
},
get webviewResourceRoot() {
checkProposedApiEnabled(extension);
return initData.environment.webviewResourceRoot;
},
get remoteName() {
if (!initData.remote.authority) {
return undefined;
......
......@@ -190,7 +190,6 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: remoteExtensionHostData.globalStorageHome,
userHome: remoteExtensionHostData.userHome,
webviewResourceRoot: this._environmentService.webviewResourceRoot,
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : {
configuration: workspace.configuration,
......
......@@ -400,7 +400,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
globalStorageHome: URI.file(this._environmentService.globalStorageHome),
userHome: URI.file(this._environmentService.userHome),
webviewResourceRoot: this._environmentService.webviewResourceRoot,
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
configuration: withNullAsUndefined(workspace.configuration),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册