Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)

* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973

* disable strict null check
This commit is contained in:
Anthony Dresser
2019-07-15 22:35:46 -07:00
committed by GitHub
parent f720ec642f
commit 0b7e7ddbf9
2406 changed files with 59140 additions and 35464 deletions

View File

@@ -5,17 +5,20 @@
import * as path from 'path';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { OpenDocumentLinkCommand } from '../commands/openDocumentLink';
import { getUriForLinkWithKnownExternalScheme } from '../util/links';
function normalizeLink(
const localize = nls.loadMessageBundle();
function parseLink(
document: vscode.TextDocument,
link: string,
base: string
): vscode.Uri {
): { uri: vscode.Uri, tooltip?: string } {
const externalSchemeUri = getUriForLinkWithKnownExternalScheme(link);
if (externalSchemeUri) {
return externalSchemeUri;
return { uri: externalSchemeUri };
}
// Assume it must be an relative or absolute file path
@@ -34,7 +37,10 @@ function normalizeLink(
resourcePath = base ? path.join(base, tempUri.path) : tempUri.path;
}
return OpenDocumentLinkCommand.createCommandUri(resourcePath, tempUri.fragment);
return {
uri: OpenDocumentLinkCommand.createCommandUri(resourcePath, tempUri.fragment),
tooltip: localize('documentLink.tooltip', 'Follow link')
};
}
function matchAll(
@@ -61,18 +67,21 @@ function extractDocumentLink(
const linkStart = document.positionAt(offset);
const linkEnd = document.positionAt(offset + link.length);
try {
return new vscode.DocumentLink(
const { uri, tooltip } = parseLink(document, link, base);
const documentLink = new vscode.DocumentLink(
new vscode.Range(linkStart, linkEnd),
normalizeLink(document, link, base));
uri);
documentLink.tooltip = tooltip;
return documentLink;
} catch (e) {
return undefined;
}
}
export default class LinkProvider implements vscode.DocumentLinkProvider {
private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|[^\]]*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g;
private readonly referenceLinkPattern = /(\[([^\]]+)\]\[\s*?)([^\s\]]*?)\]/g;
private readonly definitionPattern = /^([\t ]*\[([^\]]+)\]:\s*)(\S+)/gm;
private readonly linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\(\S*?\))+)\s*(".*?")?\)/g;
private readonly referenceLinkPattern = /(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]/g;
private readonly definitionPattern = /^([\t ]*\[((?:\\\]|[^\]])+)\]:\s*)(\S+)/gm;
public provideDocumentLinks(
document: vscode.TextDocument,
@@ -144,11 +153,10 @@ export default class LinkProvider implements vscode.DocumentLinkProvider {
}
}
for (const definition of Array.from(definitions.values())) {
for (const definition of definitions.values()) {
try {
results.push(new vscode.DocumentLink(
definition.linkRange,
normalizeLink(document, definition.link, base)));
const { uri } = parseLink(document, definition.link, base);
results.push(new vscode.DocumentLink(definition.linkRange, uri));
} catch (e) {
// noop
}

View File

@@ -16,6 +16,7 @@ import { MarkdownPreviewConfigurationManager } from './previewConfig';
import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions';
import { isMarkdownFile } from '../util/file';
import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink';
import { WebviewResourceProvider, normalizeResource } from '../util/resources';
const localize = nls.loadMessageBundle();
interface WebviewMessage {
@@ -345,8 +346,8 @@ export class MarkdownPreview extends Disposable {
private get iconPath() {
const root = path.join(this._contributionProvider.extensionPath, 'media');
return {
light: vscode.Uri.file(path.join(root, 'Preview.svg')),
dark: vscode.Uri.file(path.join(root, 'Preview_inverse.svg'))
light: vscode.Uri.file(path.join(root, 'preview-light.svg')),
dark: vscode.Uri.file(path.join(root, 'preview-dark.svg'))
};
}
@@ -388,31 +389,46 @@ export class MarkdownPreview extends Disposable {
}
private async doUpdate(): Promise<void> {
const resource = this._resource;
if (this._disposed) {
return;
}
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;
}
const pendingVersion = new PreviewDocumentVersion(resource, document.version);
if (this._disposed) {
return;
}
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) {
const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state);
if (this._resource === markdownResource) {
const self = this;
const resourceProvider: WebviewResourceProvider = {
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);
// Another call to `doUpdate` may have happened.
// Make sure we are still updating for the correct document
if (this.currentVersion && this.currentVersion.equals(pendingVersion)) {
@@ -432,21 +448,19 @@ export class MarkdownPreview extends Disposable {
}
private static getLocalResourceRoots(
resource: vscode.Uri,
base: vscode.Uri,
contributions: MarkdownContributions
): ReadonlyArray<vscode.Uri> {
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) {

View File

@@ -3,17 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as path from 'path';
import { MarkdownEngine } from '../markdownEngine';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import { Logger } from '../logger';
import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security';
import { MarkdownPreviewConfigurationManager, MarkdownPreviewConfiguration } from './previewConfig';
import { MarkdownEngine } from '../markdownEngine';
import { MarkdownContributionProvider } from '../markdownExtensions';
import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security';
import { WebviewResourceProvider } from '../util/resources';
import { MarkdownPreviewConfiguration, MarkdownPreviewConfigurationManager } from './previewConfig';
const localize = nls.loadMessageBundle();
/**
* Strings used inside the markdown preview.
@@ -35,8 +35,8 @@ const previewStrings = {
'Content Disabled Security Warning')
};
function escapeAttribute(value: string): string {
return value.replace(/"/g, '&quot;');
function escapeAttribute(value: string | vscode.Uri): string {
return value.toString().replace(/"/g, '&quot;');
}
export class MarkdownContentProvider {
@@ -50,6 +50,7 @@ export class MarkdownContentProvider {
public async provideTextDocumentContent(
markdownDocument: vscode.TextDocument,
resourceProvider: WebviewResourceProvider,
previewConfigurations: MarkdownPreviewConfigurationManager,
initialLine: number | undefined = undefined,
state?: any
@@ -63,18 +64,19 @@ export class MarkdownContentProvider {
scrollPreviewWithEditor: config.scrollPreviewWithEditor,
scrollEditorWithPreview: config.scrollEditorWithPreview,
doubleClickToSwitchToEditor: config.doubleClickToSwitchToEditor,
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings()
disableSecurityWarnings: this.cspArbiter.shouldDisableSecurityWarnings(),
webviewResourceRoot: resourceProvider.toWebviewResource(markdownDocument.uri).toString(),
};
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.getCsp(resourceProvider, sourceUri, nonce);
const body = await this.engine.render(markdownDocument);
return `<!DOCTYPE html>
<html>
<html style="${escapeAttribute(this.getSettingsOverrideStyles(config))}">
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
${csp}
@@ -82,14 +84,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="${markdownDocument.uri.with({ scheme: 'vscode-resource' }).toString(true)}">
<script src="${this.extensionResourcePath(resourceProvider, 'pre.js')}" nonce="${nonce}"></script>
${this.getStyles(resourceProvider, sourceUri, config, state)}
<base href="${resourceProvider.toWebviewResource(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(resourceProvider, nonce)}
</body>
</html>`;
}
@@ -107,13 +109,13 @@ export class MarkdownContentProvider {
</html>`;
}
private extensionResourcePath(mediaFile: string): string {
return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))
.with({ scheme: 'vscode-resource' })
.toString();
private extensionResourcePath(resourceProvider: WebviewResourceProvider, mediaFile: string): string {
const webviewResource = resourceProvider.toWebviewResource(
vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))));
return webviewResource.toString();
}
private fixHref(resource: vscode.Uri, href: string): string {
private fixHref(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, href: string): string {
if (!href) {
return href;
}
@@ -124,42 +126,36 @@ export class MarkdownContentProvider {
// Assume it must be a local file
if (path.isAbsolute(href)) {
return vscode.Uri.file(href)
.with({ scheme: 'vscode-resource' })
.toString();
return resourceProvider.toWebviewResource(vscode.Uri.file(href)).toString();
}
// Use a workspace relative path if there is a workspace
const root = vscode.workspace.getWorkspaceFolder(resource);
if (root) {
return vscode.Uri.file(path.join(root.uri.fsPath, href))
.with({ scheme: 'vscode-resource' })
.toString();
return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(root.uri.fsPath, href))).toString();
}
// Otherwise look relative to the markdown file
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))
.with({ scheme: 'vscode-resource' })
.toString();
return resourceProvider.toWebviewResource(vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString();
}
private computeCustomStyleSheetIncludes(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">`;
}).join('\n');
private computeCustomStyleSheetIncludes(resourceProvider: WebviewResourceProvider, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string {
if (!Array.isArray(config.styles)) {
return '';
}
return '';
const out: string[] = [];
for (const style of config.styles) {
out.push(`<link rel="stylesheet" class="code-user-style" data-source="${escapeAttribute(style)}" href="${escapeAttribute(this.fixHref(resourceProvider, resource, style))}" type="text/css" media="screen">`);
}
return out.join('\n');
}
private getSettingsOverrideStyles(nonce: string, config: MarkdownPreviewConfiguration): string {
return `<style nonce="${nonce}">
html, body {
${config.fontFamily ? `font-family: ${config.fontFamily};` : ''}
${isNaN(config.fontSize) ? '' : `font-size: ${config.fontSize}px;`}
${isNaN(config.lineHeight) ? '' : `line-height: ${config.lineHeight};`}
}
</style>`;
private getSettingsOverrideStyles(config: MarkdownPreviewConfiguration): string {
return [
config.fontFamily ? `--vscode-markdown-font-family: ${config.fontFamily};` : '',
isNaN(config.fontSize) ? '' : `--vscode-markdown-font-size: ${config.fontSize}px;`,
isNaN(config.lineHeight) ? '' : `--vscode-markdown-line-height: ${config.lineHeight};`,
].join(' ');
}
private getImageStabilizerStyles(state?: any) {
@@ -177,37 +173,47 @@ export class MarkdownContentProvider {
return ret;
}
private getStyles(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())}">`)
.join('\n');
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))}">`);
}
return `${baseStyles}
${this.getSettingsOverrideStyles(nonce, config)}
${this.computeCustomStyleSheetIncludes(resource, config)}
return `${baseStyles.join('\n')}
${this.computeCustomStyleSheetIncludes(resourceProvider, resource, config)}
${this.getImageStabilizerStyles(state)}`;
}
private getScripts(nonce: string): string {
return this.contributionProvider.contributions.previewScripts
.map(resource => `<script async src="${escapeAttribute(resource.toString())}" nonce="${nonce}" charset="UTF-8"></script>`)
.join('\n');
private getScripts(resourceProvider: WebviewResourceProvider, nonce: string): string {
const out: string[] = [];
for (const resource of this.contributionProvider.contributions.previewScripts) {
out.push(`<script async
src="${escapeAttribute(resourceProvider.toWebviewResource(resource))}"
nonce="${nonce}"
charset="UTF-8"></script>`);
}
return out.join('\n');
}
private getCspForResource(resource: vscode.Uri, nonce: string): string {
private getCsp(
provider: WebviewResourceProvider,
resource: vscode.Uri,
nonce: string
): string {
const rule = provider.cspSource;
switch (this.cspArbiter.getSecurityLevelForResource(resource)) {
case MarkdownPreviewSecurityLevel.AllowInsecureContent:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: http: https: data:; media-src vscode-resource: http: https: data:; script-src 'nonce-${nonce}'; style-src vscode-resource: 'unsafe-inline' http: https: data:; font-src vscode-resource: http: https: data:;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${rule} http: https: data:; media-src 'self' ${rule} http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${rule} 'unsafe-inline' http: https: data:; font-src 'self' ${rule} http: https: data:;">`;
case MarkdownPreviewSecurityLevel.AllowInsecureLocalContent:
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*; media-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src vscode-resource: 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${rule} https: data: http://localhost:* http://127.0.0.1:*; media-src 'self' ${rule} https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src 'self' ${rule} 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src 'self' ${rule} 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 vscode-resource: https: data:; media-src vscode-resource: https: data:; script-src 'nonce-${nonce}'; style-src vscode-resource: 'unsafe-inline' https: data:; font-src vscode-resource: https: data:;">`;
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' ${rule} https: data:; media-src 'self' ${rule} https: data:; script-src 'nonce-${nonce}'; style-src 'self' ${rule} 'unsafe-inline' https: data:; font-src 'self' ${rule} https: data:;">`;
}
}
}