From 2d1f6d422b7a08ec391fcedb615006f70a7e7ea1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 21 Feb 2018 14:04:35 -0800 Subject: [PATCH] Add localResourceRoots to webview options Fixes #44039 Add a new option that lets extensions override which root folders a webview can load local resources from. Defaults to allowing any resource in the workspace --- .../src/features/previewContentProvider.ts | 20 ++++++++++++++++++- src/vs/vscode.proposed.d.ts | 11 +++++++++- .../api/electron-browser/mainThreadWebview.ts | 5 +++-- .../extensions/browser/extensionEditor.ts | 6 ++---- .../parts/html/browser/html.contribution.ts | 1 + .../parts/html/browser/htmlPreviewPart.ts | 5 +---- .../workbench/parts/html/browser/webview.ts | 16 +++++++-------- .../electron-browser/releaseNotesEditor.ts | 5 +---- 8 files changed, 44 insertions(+), 25 deletions(-) diff --git a/extensions/markdown/src/features/previewContentProvider.ts b/extensions/markdown/src/features/previewContentProvider.ts index a0356d51ce47b..a942b4e16e63c 100644 --- a/extensions/markdown/src/features/previewContentProvider.ts +++ b/extensions/markdown/src/features/previewContentProvider.ts @@ -354,7 +354,10 @@ export class MarkdownPreviewWebviewManager { const view = vscode.window.createWebview( localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)), viewColumn, - { enableScripts: true }); + { + enableScripts: true, + localResourceRoots: this.getLocalResourceRoots(resource) + }); this.contentProvider.provideTextDocumentContent(resource, this.previewConfigurations).then(x => view.html = x); @@ -373,4 +376,19 @@ export class MarkdownPreviewWebviewManager { this.webviews.set(resource.fsPath, view); return view; } + + private getLocalResourceRoots( + resource: vscode.Uri + ): vscode.Uri[] { + const folder = vscode.workspace.getWorkspaceFolder(resource); + if (folder) { + return [folder.uri]; + } + + if (!resource.scheme || resource.scheme === 'file') { + return [vscode.Uri.parse(path.dirname(resource.fsPath))]; + } + + return []; + } } \ No newline at end of file diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index dc2a142150c12..a3be8f6188820 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -396,7 +396,7 @@ declare module 'vscode' { */ export interface WebviewOptions { /** - * Should scripts be enabled in the webview contetn? + * Should scripts be enabled in the webview content? * * Defaults to false (scripts-disabled). */ @@ -423,6 +423,15 @@ declare module 'vscode' { * webview content cannot be quickly saved and restored. */ readonly keepAlive?: boolean; + + /** + * Root paths from which the webview can load local (filesystem) resources using the `vscode-workspace-resource:` scheme. + * + * Default to the root folders of the current workspace. + * + * Pass in an empty array to disallow access to any local resources. + */ + readonly localResourceRoots?: Uri[]; } /** diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 57d40fb3cb211..6cbfd960fae5d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -201,7 +201,9 @@ class WebviewEditor extends BaseWebviewEditor { this.webview.options = { allowScripts: input.options.enableScripts, - enableWrappedPostMessage: true + enableWrappedPostMessage: true, + useSameOriginForRoot: false, + localResourceRoots: (input && input.options.localResourceRoots) || this._contextService.getWorkspace().folders.map(x => x.uri) }; this.webview.contents = input.html; } @@ -215,7 +217,6 @@ class WebviewEditor extends BaseWebviewEditor { this._partService.getContainer(Parts.EDITOR_PART), this.themeService, this._environmentService, - this._contextService, this._contextViewService, this.contextKey, this.findInputFocusContextKey, diff --git a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts index e6d5499598af2..12432f28cebaf 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionEditor.ts @@ -52,7 +52,6 @@ import { Color } from 'vs/base/common/color'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { assign } from 'vs/base/common/objects'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { INotificationService } from 'vs/platform/notification/common/notification'; /** A context key that is set when an extension editor webview has focus. */ @@ -194,8 +193,7 @@ export class ExtensionEditor extends BaseEditor { @IContextViewService private readonly contextViewService: IContextViewService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService + @IEnvironmentService private readonly environmentService: IEnvironmentService ) { super(ExtensionEditor.ID, telemetryService, themeService); @@ -421,7 +419,7 @@ export class ExtensionEditor extends BaseEditor { .then(body => { const allowedBadgeProviders = this.extensionsWorkbenchService.allowedBadgeProviders; const webViewOptions = allowedBadgeProviders.length > 0 ? { allowScripts: false, allowSvgs: false, svgWhiteList: allowedBadgeProviders } : {}; - this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions); + this.activeWebview = new Webview(this.content, this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, this.contextViewService, this.contextKey, this.findInputFocusContextKey, webViewOptions); const removeLayoutParticipant = arrays.insert(this.layoutParticipants, this.activeWebview); this.contentDisposables.push(toDisposable(removeLayoutParticipant)); this.activeWebview.contents = body; diff --git a/src/vs/workbench/parts/html/browser/html.contribution.ts b/src/vs/workbench/parts/html/browser/html.contribution.ts index 16303e15d9eeb..4aa5a6481f591 100644 --- a/src/vs/workbench/parts/html/browser/html.contribution.ts +++ b/src/vs/workbench/parts/html/browser/html.contribution.ts @@ -30,6 +30,7 @@ function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI } // --- Register Editor + (Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor( HtmlPreviewPart, HtmlPreviewPart.ID, diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts index 3954250506252..5e6aa8ac6a4cd 100644 --- a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -26,7 +26,6 @@ import { Webview, WebviewOptions } from './webview'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { WebviewEditor } from './webviewEditor'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; /** @@ -55,8 +54,7 @@ export class HtmlPreviewPart extends WebviewEditor { @IOpenerService private readonly openerService: IOpenerService, @IPartService private readonly partService: IPartService, @IContextViewService private readonly _contextViewService: IContextViewService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService + @IEnvironmentService private readonly _environmentService: IEnvironmentService ) { super(HtmlPreviewPart.ID, telemetryService, themeService, storageService, contextKeyService); } @@ -93,7 +91,6 @@ export class HtmlPreviewPart extends WebviewEditor { this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this._environmentService, - this._contextService, this._contextViewService, this.contextKey, this.findInputFocusContextKey, diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index 1775cad5e66d1..00669ce6dad10 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -17,7 +17,6 @@ import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { normalize, nativeSep } from 'vs/base/common/paths'; import { startsWith } from 'vs/base/common/strings'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export interface WebviewOptions { readonly allowScripts?: boolean; @@ -25,6 +24,7 @@ export interface WebviewOptions { readonly svgWhiteList?: string[]; readonly enableWrappedPostMessage?: boolean; readonly useSameOriginForRoot?: boolean; + readonly localResourceRoots?: URI[]; } export class Webview { @@ -41,7 +41,6 @@ export class Webview { private readonly _styleElement: Element, private readonly _themeService: IThemeService, private readonly _environmentService: IEnvironmentService, - private readonly _contextService: IWorkspaceContextService, private readonly _contextViewService: IContextViewService, private readonly _contextKey: IContextKey, private readonly _findInputContextKey: IContextKey, @@ -321,16 +320,16 @@ export class Webview { return; } - registerFileProtocol(contents, 'vscode-core-resource', [ + registerFileProtocol(contents, 'vscode-core-resource', () => [ this._environmentService.appRoot ]); - registerFileProtocol(contents, 'vscode-extension-resource', [ + registerFileProtocol(contents, 'vscode-extension-resource', () => [ this._environmentService.extensionsPath, this._environmentService.appRoot, this._environmentService.extensionDevelopmentPath ]); - registerFileProtocol(contents, 'vscode-workspace-resource', - this._contextService.getWorkspace().folders.map(folder => folder.uri.fsPath) + registerFileProtocol(contents, 'vscode-workspace-resource', () => + this._options.localResourceRoots.map(uri => uri.fsPath) ); } @@ -352,7 +351,6 @@ export class Webview { this._findStarted = true; this._webview.findInPage(value, findOptions); - return; } /** @@ -425,11 +423,11 @@ namespace ApiThemeClassName { function registerFileProtocol( contents: Electron.WebContents, protocol: string, - roots: string[] + getRoots: () => string[] ) { contents.session.protocol.registerFileProtocol(protocol, (request, callback: any) => { const requestPath = URI.parse(request.url).fsPath; - for (const root of roots) { + for (const root of getRoots()) { const normalizedPath = normalize(requestPath, true); if (startsWith(normalizedPath, root + nativeSep)) { callback({ path: normalizedPath }); diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index 0c81a3376dd9a..04a9a71e29289 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -28,7 +28,6 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { onUnexpectedError } from 'vs/base/common/errors'; import { addGAParameters } from 'vs/platform/telemetry/node/telemetryNodeUtils'; import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; function renderBody( body: string, @@ -64,8 +63,7 @@ export class ReleaseNotesEditor extends WebviewEditor { @IOpenerService private readonly openerService: IOpenerService, @IModeService private readonly modeService: IModeService, @IPartService private readonly partService: IPartService, - @IContextViewService private readonly _contextViewService: IContextViewService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService + @IContextViewService private readonly _contextViewService: IContextViewService ) { super(ReleaseNotesEditor.ID, telemetryService, themeService, storageService, contextKeyService); } @@ -93,7 +91,6 @@ export class ReleaseNotesEditor extends WebviewEditor { this.partService.getContainer(Parts.EDITOR_PART), this.themeService, this.environmentService, - this._contextService, this._contextViewService, this.contextKey, this.findInputFocusContextKey,