diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index b41bcf131e8..5edc8c1eb7a 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -16,7 +16,7 @@ import { MarkdownPreviewConfigurationManager } from './previewConfig'; import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; import { isMarkdownFile } from '../util/file'; import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; -import { WebviewResourceProvider } from '../util/resources'; +import { WebviewResourceProvider, normalizeResource } from '../util/resources'; const localize = nls.loadMessageBundle(); interface WebviewMessage { @@ -393,14 +393,14 @@ export class MarkdownPreview extends Disposable { return; } - const resource = this._resource; + const markdownResource = this._resource; clearTimeout(this.throttleTimer); this.throttleTimer = undefined; let document: vscode.TextDocument; try { - document = await vscode.workspace.openTextDocument(resource); + document = await vscode.workspace.openTextDocument(markdownResource); } catch { await this.showFileNotFoundError(); return; @@ -410,20 +410,22 @@ export class MarkdownPreview extends Disposable { return; } - const pendingVersion = new PreviewDocumentVersion(resource, document.version); + const pendingVersion = new PreviewDocumentVersion(markdownResource, document.version); if (!this.forceUpdate && this.currentVersion && this.currentVersion.equals(pendingVersion)) { if (this.line) { - this.updateForView(resource, this.line); + this.updateForView(markdownResource, this.line); } return; } this.forceUpdate = false; this.currentVersion = pendingVersion; - if (this._resource === resource) { + if (this._resource === markdownResource) { const self = this; const resourceProvider: WebviewResourceProvider = { - toWebviewResource: (resource) => this.editor.webview.toWebviewResource(resource), + toWebviewResource: (resource) => { + return this.editor.webview.toWebviewResource(normalizeResource(markdownResource, resource)); + }, get cspSource() { return self.editor.webview.cspSource; } }; const content = await this._contentProvider.provideTextDocumentContent(document, resourceProvider, this._previewConfigurations, this.line, this.state); @@ -446,21 +448,19 @@ export class MarkdownPreview extends Disposable { } private static getLocalResourceRoots( - resource: vscode.Uri, + base: vscode.Uri, contributions: MarkdownContributions ): ReadonlyArray { - const baseRoots = contributions.previewResourceRoots; + const baseRoots = Array.from(contributions.previewResourceRoots); - const folder = vscode.workspace.getWorkspaceFolder(resource); + const folder = vscode.workspace.getWorkspaceFolder(base); if (folder) { - return baseRoots.concat(folder.uri); + baseRoots.push(folder.uri); + } else if (!base.scheme || base.scheme === 'file') { + baseRoots.push(vscode.Uri.file(path.dirname(base.fsPath))); } - if (!resource.scheme || resource.scheme === 'file') { - return baseRoots.concat(vscode.Uri.file(path.dirname(resource.fsPath))); - } - - return baseRoots; + return baseRoots.map(root => normalizeResource(base, root)); } private onDidScrollPreview(line: number) { diff --git a/extensions/markdown-language-features/src/util/resources.ts b/extensions/markdown-language-features/src/util/resources.ts index 91fac463a2c..1def7adcce0 100644 --- a/extensions/markdown-language-features/src/util/resources.ts +++ b/extensions/markdown-language-features/src/util/resources.ts @@ -9,4 +9,25 @@ export interface WebviewResourceProvider { toWebviewResource(resource: vscode.Uri): vscode.Uri; readonly cspSource: string; +} + +export function normalizeResource( + base: vscode.Uri, + resource: vscode.Uri +): vscode.Uri { + // If we have a windows path and are loading a workspace with an authority, + // make sure we use a unc path with an explicit localhost authority. + // + // Otherwise, the `` rule will insert the authority into the resolved resource + // URI incorrectly. + if (base.authority && !resource.authority) { + const driveMatch = resource.path.match(/^\/(\w):\//); + if (driveMatch) { + return vscode.Uri.file(`\\\\localhost\\${driveMatch[1]}$\\${resource.fsPath.replace(/^\w:\\/, '')}`).with({ + fragment: resource.fragment, + query: resource.query + }); + } + } + return resource; } \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/common/resourceLoader.ts b/src/vs/workbench/contrib/webview/common/resourceLoader.ts index f852c52b9a7..ea33db2e268 100644 --- a/src/vs/workbench/contrib/webview/common/resourceLoader.ts +++ b/src/vs/workbench/contrib/webview/common/resourceLoader.ts @@ -45,11 +45,14 @@ export async function loadLocalResource( extensionLocation: URI | undefined, getRoots: () => ReadonlyArray ): Promise { - const requestPath = requestUri.authority ? `//${requestUri.authority}${requestUri.path}` : requestUri.path; - const normalizedPath = URI.file(requestPath); + const normalizedPath = requestUri.with({ + scheme: 'file', + fragment: '', + query: '', + }); + for (const root of getRoots()) { - const rootPath = root.fsPath + (endsWith(root.fsPath, sep) ? '' : sep); - if (!startsWith(normalizedPath.fsPath, rootPath)) { + if (!containsResource(root, normalizedPath)) { continue; } @@ -70,3 +73,8 @@ export async function loadLocalResource( return AccessDenied; } + +function containsResource(root: URI, resource: URI): boolean { + const rootPath = root.fsPath + (endsWith(root.fsPath, sep) ? '' : sep); + return startsWith(resource.fsPath, rootPath); +}