From aa3cbbd165255d22907d98eb748339a5677924e6 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sat, 30 Oct 2021 17:47:16 +0100 Subject: [PATCH] Desktop, Mobile: Fixes #5593: Do not render very large code blocks to prevent app from freezing --- packages/renderer/MdToHtml.ts | 61 +++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/renderer/MdToHtml.ts b/packages/renderer/MdToHtml.ts index 6a81c04e8a..db86baf206 100644 --- a/packages/renderer/MdToHtml.ts +++ b/packages/renderer/MdToHtml.ts @@ -393,6 +393,24 @@ export default class MdToHtml { this.cachedOutputs_ = {}; } + private removeLastNewLine(s: string): string { + if (s[s.length - 1] === '\n') { + return s.substr(0, s.length - 1); + } else { + return s; + } + } + + // Rendering large code blocks can freeze the app so we disable it in + // certain cases: + // https://github.com/laurent22/joplin/issues/5593#issuecomment-947374218 + private shouldSkipHighlighting(str: string, lang: string): boolean { + if (lang && !hljs.getLanguage(lang)) lang = ''; + if (str.length >= 1000 && !lang) return true; + if (str.length >= 512000 && lang) return true; + return false; + } + // "theme" is the theme as returned by themeStyle() public async render(body: string, theme: any = null, options: RenderOptions = null): Promise { @@ -455,29 +473,32 @@ export default class MdToHtml { // The strings includes the last \n that is part of the fence, // so we remove it because we need the exact code in the source block - const trimmedStr = str.replace(/(.*)\n$/, '$1'); + const trimmedStr = this.removeLastNewLine(str); const sourceBlockHtml = `
${markdownIt.utils.escapeHtml(trimmedStr)}
`; - try { - let hlCode = ''; - - const cacheKey = md5(`${str}_${lang}`); - - if (options.codeHighlightCacheKey && this.cachedHighlightedCode_[cacheKey]) { - hlCode = this.cachedHighlightedCode_[cacheKey]; - } else { - if (lang && hljs.getLanguage(lang)) { - // hlCode = hljs.highlight(lang, trimmedStr, true).value; - hlCode = hljs.highlight(trimmedStr, { language: lang, ignoreIllegals: true }).value; - } else { - hlCode = hljs.highlightAuto(trimmedStr).value; - } - this.cachedHighlightedCode_[cacheKey] = hlCode; - } - - outputCodeHtml = hlCode; - } catch (error) { + if (this.shouldSkipHighlighting(trimmedStr, lang)) { outputCodeHtml = markdownIt.utils.escapeHtml(trimmedStr); + } else { + try { + let hlCode = ''; + + const cacheKey = md5(`${str}_${lang}`); + + if (options.codeHighlightCacheKey && this.cachedHighlightedCode_[cacheKey]) { + hlCode = this.cachedHighlightedCode_[cacheKey]; + } else { + if (lang && hljs.getLanguage(lang)) { + hlCode = hljs.highlight(trimmedStr, { language: lang, ignoreIllegals: true }).value; + } else { + hlCode = hljs.highlightAuto(trimmedStr).value; + } + this.cachedHighlightedCode_[cacheKey] = hlCode; + } + + outputCodeHtml = hlCode; + } catch (error) { + outputCodeHtml = markdownIt.utils.escapeHtml(trimmedStr); + } } const html = `
${sourceBlockHtml}
${outputCodeHtml}
`;