mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 13:29:44 +00:00
use tt policy when rendering html from markdown, https://github.com/microsoft/vscode/issues/106396
This commit is contained in:
parent
93a1dab31f
commit
a8cf19d34a
117
.vscode/searches/TrustedTypes.code-search
vendored
117
.vscode/searches/TrustedTypes.code-search
vendored
|
@ -4,24 +4,27 @@
|
||||||
# Excluding: *.test.ts
|
# Excluding: *.test.ts
|
||||||
# ContextLines: 3
|
# ContextLines: 3
|
||||||
|
|
||||||
22 results - 14 files
|
22 results - 15 files
|
||||||
|
|
||||||
|
src/vs/base/browser/dom.ts:
|
||||||
|
26 // this is a workaround for innerHTML not allowing for "asymetric" accessors
|
||||||
|
27 // see https://github.com/microsoft/vscode/issues/106396#issuecomment-692625393
|
||||||
|
28 // and https://github.com/microsoft/TypeScript/issues/30024
|
||||||
|
29: node.innerHTML = value as unknown as string;
|
||||||
|
30 }
|
||||||
|
31
|
||||||
|
32 export function isInDOM(node: Node | null): boolean {
|
||||||
|
|
||||||
src/vs/base/browser/markdownRenderer.ts:
|
src/vs/base/browser/markdownRenderer.ts:
|
||||||
161 const strValue = values[0];
|
273 };
|
||||||
162 const span = element.querySelector(`div[data-code="${id}"]`);
|
274
|
||||||
163 if (span) {
|
275 if (_ttpInsane) {
|
||||||
164: span.innerHTML = strValue;
|
276: element.innerHTML = _ttpInsane.createHTML(renderedMarkdown, insaneOptions) as unknown as string;
|
||||||
165 }
|
277 } else {
|
||||||
166 }).catch(err => {
|
278: element.innerHTML = insane(renderedMarkdown, insaneOptions);
|
||||||
167 // ignore
|
279 }
|
||||||
|
280
|
||||||
243 return true;
|
281 // signal that async code blocks can be now be inserted
|
||||||
244 }
|
|
||||||
245
|
|
||||||
246: element.innerHTML = insane(renderedMarkdown, {
|
|
||||||
247 allowedSchemes,
|
|
||||||
248 // allowedTags should included everything that markdown renders to.
|
|
||||||
249 // Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure.
|
|
||||||
|
|
||||||
src/vs/base/browser/ui/contextview/contextview.ts:
|
src/vs/base/browser/ui/contextview/contextview.ts:
|
||||||
157 this.shadowRootHostElement = DOM.$('.shadow-root-host');
|
157 this.shadowRootHostElement = DOM.$('.shadow-root-host');
|
||||||
|
@ -50,6 +53,15 @@ src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts:
|
||||||
326 document.head.appendChild(styleTag);
|
326 document.head.appendChild(styleTag);
|
||||||
327 }
|
327 }
|
||||||
|
|
||||||
|
src/vs/editor/browser/core/markdownRenderer.ts:
|
||||||
|
88
|
||||||
|
89 const element = document.createElement('span');
|
||||||
|
90
|
||||||
|
91: element.innerHTML = MarkdownRenderer._ttpTokenizer
|
||||||
|
92 ? MarkdownRenderer._ttpTokenizer.createHTML(value, tokenization) as unknown as string
|
||||||
|
93 : tokenizeToString(value, tokenization);
|
||||||
|
94
|
||||||
|
|
||||||
src/vs/editor/browser/view/domLineBreaksComputer.ts:
|
src/vs/editor/browser/view/domLineBreaksComputer.ts:
|
||||||
107 allCharOffsets[i] = tmp[0];
|
107 allCharOffsets[i] = tmp[0];
|
||||||
108 allVisibleColumns[i] = tmp[1];
|
108 allVisibleColumns[i] = tmp[1];
|
||||||
|
@ -60,21 +72,21 @@ src/vs/editor/browser/view/domLineBreaksComputer.ts:
|
||||||
113 containerDomNode.style.top = '10000';
|
113 containerDomNode.style.top = '10000';
|
||||||
|
|
||||||
src/vs/editor/browser/view/viewLayer.ts:
|
src/vs/editor/browser/view/viewLayer.ts:
|
||||||
507 private _finishRenderingNewLines(ctx: IRendererContext<T>, domNodeIsEmpty: boolean, newLinesHTML: string, wasNew: boolean[]): void {
|
512 }
|
||||||
508 const lastChild = <HTMLElement>this.domNode.lastChild;
|
513 const lastChild = <HTMLElement>this.domNode.lastChild;
|
||||||
509 if (domNodeIsEmpty || !lastChild) {
|
514 if (domNodeIsEmpty || !lastChild) {
|
||||||
510: this.domNode.innerHTML = newLinesHTML;
|
515: this.domNode.innerHTML = newLinesHTML;
|
||||||
511 } else {
|
516 } else {
|
||||||
512 lastChild.insertAdjacentHTML('afterend', newLinesHTML);
|
517 lastChild.insertAdjacentHTML('afterend', newLinesHTML);
|
||||||
513 }
|
518 }
|
||||||
|
|
||||||
525 private _finishRenderingInvalidLines(ctx: IRendererContext<T>, invalidLinesHTML: string, wasInvalid: boolean[]): void {
|
530 private _finishRenderingInvalidLines(ctx: IRendererContext<T>, invalidLinesHTML: string, wasInvalid: boolean[]): void {
|
||||||
526 const hugeDomNode = document.createElement('div');
|
531 const hugeDomNode = document.createElement('div');
|
||||||
527
|
532
|
||||||
528: hugeDomNode.innerHTML = invalidLinesHTML;
|
533: hugeDomNode.innerHTML = invalidLinesHTML;
|
||||||
529
|
534
|
||||||
530 for (let i = 0; i < ctx.linesLength; i++) {
|
535 for (let i = 0; i < ctx.linesLength; i++) {
|
||||||
531 const line = ctx.lines[i];
|
536 const line = ctx.lines[i];
|
||||||
|
|
||||||
src/vs/editor/browser/widget/diffEditorWidget.ts:
|
src/vs/editor/browser/widget/diffEditorWidget.ts:
|
||||||
2157
|
2157
|
||||||
|
@ -141,23 +153,6 @@ src/vs/editor/test/browser/controller/imeTester.ts:
|
||||||
74
|
74
|
||||||
75 let startBtn = document.createElement('button');
|
75 let startBtn = document.createElement('button');
|
||||||
|
|
||||||
src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts:
|
|
||||||
455
|
|
||||||
456 private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement {
|
|
||||||
457 const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row');
|
|
||||||
458: dragImageContainer.innerHTML = templateData.container.outerHTML;
|
|
||||||
459
|
|
||||||
460 // Remove all rendered content nodes after the
|
|
||||||
461 const markdownContent = dragImageContainer.querySelector('.cell.markdown')!;
|
|
||||||
|
|
||||||
641 return null;
|
|
||||||
642 }
|
|
||||||
643
|
|
||||||
644: editorContainer.innerHTML = richEditorText;
|
|
||||||
645
|
|
||||||
646 return dragImageContainer;
|
|
||||||
647 }
|
|
||||||
|
|
||||||
src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts:
|
src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts:
|
||||||
375 addMouseoverListeners(outputNode, outputId);
|
375 addMouseoverListeners(outputNode, outputId);
|
||||||
376 const content = data.content;
|
376 const content = data.content;
|
||||||
|
@ -177,18 +172,18 @@ src/vs/workbench/contrib/webview/browser/pre/main.js:
|
||||||
399 applyStyles(newDocument, newDocument.body);
|
399 applyStyles(newDocument, newDocument.body);
|
||||||
|
|
||||||
src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts:
|
src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts:
|
||||||
281
|
280
|
||||||
282 const content = model.main.textEditorModel.getValue(EndOfLinePreference.LF);
|
281 const content = model.main;
|
||||||
283 if (!input.resource.path.endsWith('.md')) {
|
282 if (!input.resource.path.endsWith('.md')) {
|
||||||
284: this.content.innerHTML = content;
|
283: this.content.innerHTML = content;
|
||||||
285 this.updateSizeClasses();
|
284 this.updateSizeClasses();
|
||||||
286 this.decorateContent();
|
285 this.decorateContent();
|
||||||
287 this.contentDisposables.push(this.keybindingService.onDidUpdateKeybindings(() => this.decorateContent()));
|
286 this.contentDisposables.push(this.keybindingService.onDidUpdateKeybindings(() => this.decorateContent()));
|
||||||
|
|
||||||
303 const innerContent = document.createElement('div');
|
302 const innerContent = document.createElement('div');
|
||||||
304 innerContent.classList.add('walkThroughContent'); // only for markdown files
|
303 innerContent.classList.add('walkThroughContent'); // only for markdown files
|
||||||
305 const markdown = this.expandMacros(content);
|
304 const markdown = this.expandMacros(content);
|
||||||
306: innerContent.innerHTML = marked(markdown, { renderer });
|
305: innerContent.innerHTML = marked(markdown, { renderer });
|
||||||
307 this.content.appendChild(innerContent);
|
306 this.content.appendChild(innerContent);
|
||||||
308
|
307
|
||||||
309 model.snippets.forEach((snippet, i) => {
|
308 model.snippets.forEach((snippet, i) => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||||
import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent';
|
import { IMarkdownString, parseHrefAndDimensions, removeMarkdownEscapes } from 'vs/base/common/htmlContent';
|
||||||
import { defaultGenerator } from 'vs/base/common/idGenerator';
|
import { defaultGenerator } from 'vs/base/common/idGenerator';
|
||||||
import * as marked from 'vs/base/common/marked/marked';
|
import * as marked from 'vs/base/common/marked/marked';
|
||||||
import { insane } from 'vs/base/common/insane/insane';
|
import { insane, InsaneOptions } from 'vs/base/common/insane/insane';
|
||||||
import { parse } from 'vs/base/common/marshalling';
|
import { parse } from 'vs/base/common/marshalling';
|
||||||
import { cloneAndChange } from 'vs/base/common/objects';
|
import { cloneAndChange } from 'vs/base/common/objects';
|
||||||
import { escape } from 'vs/base/common/strings';
|
import { escape } from 'vs/base/common/strings';
|
||||||
|
@ -30,6 +30,12 @@ export interface MarkdownRenderOptions extends FormattedTextRenderOptions {
|
||||||
baseUrl?: URI;
|
baseUrl?: URI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _ttpInsane = window.trustedTypes?.createPolicy('insane', {
|
||||||
|
createHTML(value, options: InsaneOptions): string {
|
||||||
|
return insane(value, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Low-level way create a html element from a markdown string.
|
* Low-level way create a html element from a markdown string.
|
||||||
*
|
*
|
||||||
|
@ -227,25 +233,16 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
|
||||||
if (value.length > 100_000) {
|
if (value.length > 100_000) {
|
||||||
value = `${value.substr(0, 100_000)}…`;
|
value = `${value.substr(0, 100_000)}…`;
|
||||||
}
|
}
|
||||||
const renderedMarkdown = marked.parse(
|
// escape theme icons
|
||||||
markdown.supportThemeIcons ? markdownEscapeEscapedCodicons(value) : value,
|
if (markdown.supportThemeIcons) {
|
||||||
markedOptions
|
value = markdownEscapeEscapedCodicons(value);
|
||||||
);
|
|
||||||
|
|
||||||
function filter(token: { tag: string, attrs: { readonly [key: string]: string } }): boolean {
|
|
||||||
if (token.tag === 'span' && markdown.isTrusted && (Object.keys(token.attrs).length === 1)) {
|
|
||||||
if (token.attrs['style']) {
|
|
||||||
return !!token.attrs['style'].match(/^(color\:#[0-9a-fA-F]+;)?(background-color\:#[0-9a-fA-F]+;)?$/);
|
|
||||||
} else if (token.attrs['class']) {
|
|
||||||
// The class should match codicon rendering in src\vs\base\common\codicons.ts
|
|
||||||
return !!token.attrs['class'].match(/^codicon codicon-[a-z\-]+( codicon-animation-[a-z\-]+)?$/);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
element.innerHTML = insane(renderedMarkdown, {
|
const renderedMarkdown = marked.parse(value, markedOptions);
|
||||||
|
|
||||||
|
|
||||||
|
// sanitize with insane
|
||||||
|
const insaneOptions = {
|
||||||
allowedSchemes,
|
allowedSchemes,
|
||||||
// allowedTags should included everything that markdown renders to.
|
// allowedTags should included everything that markdown renders to.
|
||||||
// Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure.
|
// Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure.
|
||||||
|
@ -261,9 +258,27 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
|
||||||
'th': ['align'],
|
'th': ['align'],
|
||||||
'td': ['align']
|
'td': ['align']
|
||||||
},
|
},
|
||||||
filter
|
filter(token: { tag: string, attrs: { readonly [key: string]: string } }): boolean {
|
||||||
});
|
if (token.tag === 'span' && markdown.isTrusted && (Object.keys(token.attrs).length === 1)) {
|
||||||
|
if (token.attrs['style']) {
|
||||||
|
return !!token.attrs['style'].match(/^(color\:#[0-9a-fA-F]+;)?(background-color\:#[0-9a-fA-F]+;)?$/);
|
||||||
|
} else if (token.attrs['class']) {
|
||||||
|
// The class should match codicon rendering in src\vs\base\common\codicons.ts
|
||||||
|
return !!token.attrs['class'].match(/^codicon codicon-[a-z\-]+( codicon-animation-[a-z\-]+)?$/);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_ttpInsane) {
|
||||||
|
element.innerHTML = _ttpInsane.createHTML(renderedMarkdown, insaneOptions) as unknown as string;
|
||||||
|
} else {
|
||||||
|
element.innerHTML = insane(renderedMarkdown, insaneOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// signal that async code blocks can be now be inserted
|
||||||
signalInnerHTML!();
|
signalInnerHTML!();
|
||||||
|
|
||||||
return element;
|
return element;
|
||||||
|
|
14
src/vs/base/common/insane/insane.d.ts
vendored
14
src/vs/base/common/insane/insane.d.ts
vendored
|
@ -3,13 +3,15 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
export interface InsaneOptions {
|
||||||
|
readonly allowedSchemes?: readonly string[],
|
||||||
|
readonly allowedTags?: readonly string[],
|
||||||
|
readonly allowedAttributes?: { readonly [key: string]: string[] },
|
||||||
|
readonly filter?: (token: { tag: string, attrs: { readonly [key: string]: string } }) => boolean,
|
||||||
|
}
|
||||||
|
|
||||||
export function insane(
|
export function insane(
|
||||||
html: string,
|
html: string,
|
||||||
options?: {
|
options?: InsaneOptions,
|
||||||
readonly allowedSchemes?: readonly string[],
|
|
||||||
readonly allowedTags?: readonly string[],
|
|
||||||
readonly allowedAttributes?: { readonly [key: string]: string[] },
|
|
||||||
readonly filter?: (token: { tag: string, attrs: { readonly [key: string]: string } }) => boolean,
|
|
||||||
},
|
|
||||||
strict?: boolean,
|
strict?: boolean,
|
||||||
): string;
|
): string;
|
||||||
|
|
Loading…
Reference in a new issue