You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-26 22:41:17 +02:00
Desktop: Rich Text Editor: Disallow inline event handlers (#12106)
This commit is contained in:
@@ -13,13 +13,6 @@ export default function(context) {
|
||||
const token = tokens[idx];
|
||||
if (token.info !== 'justtesting') return defaultRender(tokens, idx, options, env, self);
|
||||
|
||||
const postMessageWithResponseTest = `
|
||||
webviewApi.postMessage('${contentScriptId}', 'justtesting').then(function(response) {
|
||||
console.info('Got response in content script: ' + response);
|
||||
});
|
||||
return false;
|
||||
`;
|
||||
|
||||
// Rich text editor support:
|
||||
// The joplin-editable and joplin-source CSS classes mark the generated div
|
||||
// as a region that needs special processing when converting back to markdown.
|
||||
@@ -38,14 +31,23 @@ export default function(context) {
|
||||
${richTextEditorMetadata}
|
||||
|
||||
<p>JUST TESTING: <pre>${markdownIt.utils.escapeHtml(leftPad(token.content.trim(), 10, 'x'))}</pre></p>
|
||||
<p><a href="#" onclick="${postMessageWithResponseTest.replace(/\n/g, ' ')}">Click to post a message "justtesting" to plugin and check the response in the console</a></p>
|
||||
<p>
|
||||
<a
|
||||
href="#"
|
||||
data-content-script-id="${markdownIt.utils.escapeHtml(contentScriptId)}"
|
||||
class="post-message-link"
|
||||
>
|
||||
Click to post a message "justtesting" to plugin and check the response in the console
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
},
|
||||
assets: function() {
|
||||
return [
|
||||
{ name: 'markdownItTestPlugin.css' }
|
||||
{ name: 'markdownItTestPlugin.css' },
|
||||
{ name: 'markdownItTestPluginRuntime.js' },
|
||||
];
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
const addClickHandlers = () => {
|
||||
const postMessageLinks = document.querySelectorAll('.post-message-link');
|
||||
for (const link of postMessageLinks) {
|
||||
const contentScriptId = link.getAttribute('data-content-script-id');
|
||||
link.onclick = async () => {
|
||||
const response = await webviewApi.postMessage(contentScriptId, 'justtesting');
|
||||
link.textContent = 'Got response in content script: ' + response;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('joplin-noteDidUpdate', () => {
|
||||
addClickHandlers();
|
||||
});
|
||||
@@ -43,6 +43,7 @@ import useKeyboardRefocusHandler from './utils/useKeyboardRefocusHandler';
|
||||
import useDocument from '../../../hooks/useDocument';
|
||||
import useEditDialog from './utils/useEditDialog';
|
||||
import useEditDialogEventListeners from './utils/useEditDialogEventListeners';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import useTextPatternsLookup from './utils/useTextPatternsLookup';
|
||||
|
||||
const logger = Logger.create('TinyMCE');
|
||||
@@ -728,6 +729,25 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
language_url: ['en_US', 'en_GB'].includes(language) ? undefined : `${bridge().vendorDir()}/lib/tinymce/langs/${language}`,
|
||||
toolbar: toolbar.join(' '),
|
||||
localization_function: _,
|
||||
// See https://www.tiny.cloud/docs/tinymce/latest/tinymce-and-csp/#content_security_policy
|
||||
content_security_policy: Setting.value('featureFlag.richText.useStrictContentSecurityPolicy') ? [
|
||||
// Media: *: Allow users to include images and videos from the internet (e.g. ).
|
||||
// Media: blob: Allow loading images/videos/audio from blob URLs. The Rich Text Editor
|
||||
// replaces certain base64 URLs with blob URLs.
|
||||
// Media: data: Allow loading images and other media from data: URLs
|
||||
'default-src \'self\'',
|
||||
'img-src \'self\' blob: data: *', // Images
|
||||
'media-src \'self\' blob: data: *', // Audio and video players
|
||||
|
||||
// Disallow certain unused features
|
||||
'child-src \'none\'', // Should not contain sub-frames
|
||||
'object-src \'none\'', // Objects can be used for script injection
|
||||
'form-action \'none\'', // No submitting forms
|
||||
|
||||
// Styles: unsafe-inline: TinyMCE uses inline style="" styles.
|
||||
// Styles: *: Allow users to include styles from the internet (e.g. <style src="https://example.com/style.css">)
|
||||
'style-src \'self\' \'unsafe-inline\' * data:',
|
||||
].join(' ; ') : undefined,
|
||||
contextmenu: false,
|
||||
browser_spellcheck: true,
|
||||
|
||||
|
||||
@@ -2,72 +2,37 @@ import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import { useEffect } from 'react';
|
||||
import { Editor } from 'tinymce';
|
||||
|
||||
const useWebViewApi = (editor: Editor, window: Window) => {
|
||||
interface WebViewApi {
|
||||
postMessage: (contentScriptId: string, message: unknown)=> Promise<unknown>;
|
||||
}
|
||||
|
||||
interface ExtendedWindow extends Window {
|
||||
webviewApi: WebViewApi;
|
||||
}
|
||||
|
||||
const useWebViewApi = (editor: Editor, containerWindow: Window) => {
|
||||
useEffect(() => {
|
||||
if (!editor) return ()=>{};
|
||||
if (!window) return ()=>{};
|
||||
if (!containerWindow) return ()=>{};
|
||||
|
||||
const scriptElement = window.document.createElement('script');
|
||||
const channelId = `plugin-post-message-${Math.random()}`;
|
||||
scriptElement.appendChild(window.document.createTextNode(`
|
||||
window.webviewApi = {
|
||||
postMessage: (contentScriptId, message) => {
|
||||
const channelId = ${JSON.stringify(channelId)};
|
||||
const messageId = Math.random();
|
||||
window.parent.postMessage({
|
||||
channelId,
|
||||
messageId,
|
||||
contentScriptId,
|
||||
message,
|
||||
}, '*');
|
||||
|
||||
const waitForResponse = async () => {
|
||||
while (true) {
|
||||
const messageEvent = await new Promise(resolve => {
|
||||
window.addEventListener('message', event => {
|
||||
resolve(event);
|
||||
}, {once: true});
|
||||
});
|
||||
|
||||
if (messageEvent.source !== window.parent || messageEvent.data.messageId !== messageId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const data = messageEvent.data;
|
||||
return data.response;
|
||||
}
|
||||
};
|
||||
|
||||
return waitForResponse();
|
||||
},
|
||||
};
|
||||
`));
|
||||
const editorWindow = editor.getWin();
|
||||
editorWindow.document.head.appendChild(scriptElement);
|
||||
|
||||
const onMessageHandler = async (event: MessageEvent) => {
|
||||
if (event.source !== editorWindow || event.data.channelId !== channelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentScriptId = event.data.contentScriptId;
|
||||
const editorWindow = editor.getWin() as ExtendedWindow;
|
||||
const webviewApi: WebViewApi = {
|
||||
postMessage: async (contentScriptId: string, message: unknown) => {
|
||||
const pluginService = PluginService.instance();
|
||||
const plugin = pluginService.pluginById(
|
||||
pluginService.pluginIdByContentScriptId(contentScriptId),
|
||||
);
|
||||
const result = await plugin.emitContentScriptMessage(contentScriptId, event.data.message);
|
||||
editorWindow.postMessage({
|
||||
messageId: event.data.messageId,
|
||||
response: result,
|
||||
}, '*');
|
||||
return await plugin.emitContentScriptMessage(contentScriptId, message);
|
||||
},
|
||||
};
|
||||
window.addEventListener('message', onMessageHandler);
|
||||
editorWindow.webviewApi = webviewApi;
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', onMessageHandler);
|
||||
scriptElement.remove();
|
||||
if (editorWindow.webviewApi === webviewApi) {
|
||||
editorWindow.webviewApi = undefined;
|
||||
}
|
||||
};
|
||||
}, [editor, window]);
|
||||
}, [editor, containerWindow]);
|
||||
};
|
||||
|
||||
export default useWebViewApi;
|
||||
|
||||
@@ -163,8 +163,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
scrollbarSize: props.scrollbarSize,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const allAssets = useCallback(async (markupLanguage: number, options: AllAssetsOptions = null): Promise<any[]> => {
|
||||
const allAssets = useCallback(async (markupLanguage: number, options: AllAssetsOptions = null) => {
|
||||
options = {
|
||||
contentMaxWidthTarget: '',
|
||||
...options,
|
||||
@@ -172,7 +171,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
|
||||
const theme = themeStyle(options.themeId ? options.themeId : props.themeId);
|
||||
|
||||
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
|
||||
const markupToHtml = markupLanguageUtils.newMarkupToHtml(props.plugins, {
|
||||
resourceBaseUrl: `joplin-content://note-viewer/${Setting.value('resourceDir')}/`,
|
||||
customCss: props.customCss,
|
||||
});
|
||||
@@ -183,7 +182,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
scrollbarSize: props.scrollbarSize,
|
||||
whiteBackgroundNoteRendering: options.whiteBackgroundNoteRendering,
|
||||
});
|
||||
}, [props.themeId, props.scrollbarSize, props.customCss, props.contentMaxWidth]);
|
||||
}, [props.plugins, props.themeId, props.scrollbarSize, props.customCss, props.contentMaxWidth]);
|
||||
|
||||
const handleProvisionalFlag = useCallback(() => {
|
||||
if (props.isProvisional) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
hash:"cfa07333af79f4db4bc9ca008fb257f8", files: {
|
||||
hash:"6950f3929f5177b0bb2a0c039669810d", files: {
|
||||
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
||||
'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
||||
'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = {"hash":"cfa07333af79f4db4bc9ca008fb257f8","files":["highlight.js/atom-one-dark-reasonable.css","highlight.js/atom-one-light.css","katex/fonts/KaTeX_AMS-Regular.woff2","katex/fonts/KaTeX_Caligraphic-Bold.woff2","katex/fonts/KaTeX_Caligraphic-Regular.woff2","katex/fonts/KaTeX_Fraktur-Bold.woff2","katex/fonts/KaTeX_Fraktur-Regular.woff2","katex/fonts/KaTeX_Main-Bold.woff2","katex/fonts/KaTeX_Main-BoldItalic.woff2","katex/fonts/KaTeX_Main-Italic.woff2","katex/fonts/KaTeX_Main-Regular.woff2","katex/fonts/KaTeX_Math-BoldItalic.woff2","katex/fonts/KaTeX_Math-Italic.woff2","katex/fonts/KaTeX_SansSerif-Bold.woff2","katex/fonts/KaTeX_SansSerif-Italic.woff2","katex/fonts/KaTeX_SansSerif-Regular.woff2","katex/fonts/KaTeX_Script-Regular.woff2","katex/fonts/KaTeX_Size1-Regular.woff2","katex/fonts/KaTeX_Size2-Regular.woff2","katex/fonts/KaTeX_Size3-Regular.woff2","katex/fonts/KaTeX_Size4-Regular.woff2","katex/fonts/KaTeX_Typewriter-Regular.woff2","katex/katex.css","mermaid/mermaid.min.js","mermaid/mermaid_render.js"]}
|
||||
module.exports = {"hash":"6950f3929f5177b0bb2a0c039669810d","files":["highlight.js/atom-one-dark-reasonable.css","highlight.js/atom-one-light.css","katex/fonts/KaTeX_AMS-Regular.woff2","katex/fonts/KaTeX_Caligraphic-Bold.woff2","katex/fonts/KaTeX_Caligraphic-Regular.woff2","katex/fonts/KaTeX_Fraktur-Bold.woff2","katex/fonts/KaTeX_Fraktur-Regular.woff2","katex/fonts/KaTeX_Main-Bold.woff2","katex/fonts/KaTeX_Main-BoldItalic.woff2","katex/fonts/KaTeX_Main-Italic.woff2","katex/fonts/KaTeX_Main-Regular.woff2","katex/fonts/KaTeX_Math-BoldItalic.woff2","katex/fonts/KaTeX_Math-Italic.woff2","katex/fonts/KaTeX_SansSerif-Bold.woff2","katex/fonts/KaTeX_SansSerif-Italic.woff2","katex/fonts/KaTeX_SansSerif-Regular.woff2","katex/fonts/KaTeX_Script-Regular.woff2","katex/fonts/KaTeX_Size1-Regular.woff2","katex/fonts/KaTeX_Size2-Regular.woff2","katex/fonts/KaTeX_Size3-Regular.woff2","katex/fonts/KaTeX_Size4-Regular.woff2","katex/fonts/KaTeX_Typewriter-Regular.woff2","katex/katex.css","mermaid/mermaid.min.js","mermaid/mermaid_render.js"]}
|
||||
@@ -1 +1 @@
|
||||
module.exports = `LyogZ2xvYmFsIG1lcm1haWQgKi8KCmZ1bmN0aW9uIG1lcm1haWRSZWFkeSgpIHsKCS8vIFRoZSBNZXJtYWlkIGluaXRpYWxpemF0aW9uIGNvZGUgcmVuZGVycyB0aGUgTWVybWFpZCBjb2RlIHdpdGhpbiBhbnkgZWxlbWVudCB3aXRoIGNsYXNzICJtZXJtYWlkIiBvcgoJLy8gSUQgIm1lcm1haWQiLiBIb3dldmVyIGluIHNvbWUgY2FzZXMgc29tZSBlbGVtZW50cyBtaWdodCBoYXZlIHRoaXMgSUQgYnV0IG5vdCBiZSBNZXJtYWlkIGNvZGUuCgkvLyBGb3IgZXhhbXBsZSwgTWFya2Rvd24gY29kZSBsaWtlIHRoaXM6CgkvLwoJLy8gICAgICMgTWVybWFpZAoJLy8KCS8vIFdpbGwgZ2VuZXJhdGUgdGhpcyBIVE1MOgoJLy8KCS8vICAgICA8aDEgaWQ9Im1lcm1haWQiPk1lcm1haWQ8L2gxPgoJLy8KCS8vIEFuZCB0aGF0J3MgZ29pbmcgdG8gbWFrZSB0aGUgbGliIHNldCB0aGUgYG1lcm1haWRgIG9iamVjdCB0byB0aGUgSDEgZWxlbWVudC4KCS8vIFNvIGJlbG93LCB3ZSBkb3VibGUtY2hlY2sgdGhhdCB3aGF0IHdlIGhhdmUgcmVhbGx5IGlzIGFuIGluc3RhbmNlIG9mIHRoZSBsaWJyYXJ5LgoJcmV0dXJuIHR5cGVvZiBtZXJtYWlkICE9PSAndW5kZWZpbmVkJyAmJiBtZXJtYWlkICE9PSBudWxsICYmIHR5cGVvZiBtZXJtYWlkID09PSAnb2JqZWN0JyAmJiAhIW1lcm1haWQuaW5pdGlhbGl6ZTsKfQoKY29uc3QgaXNEYXJrTW9kZSA9ICgpID0+IHsKCS8vIElmIGFueSBtZXJtYWlkIGVsZW1lbnRzIGFyZSBtYXJrZWQgYXMgcmVxdWlyaW5nIGRhcmsgbW9kZSwgcmVuZGVyICphbGwqCgkvLyBtZXJtYWlkIGVsZW1lbnRzIGluIGRhcmsgbW9kZS4KCXJldHVybiAhIWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5tZXJtYWlkLmpvcGxpbi0tbWVybWFpZC11c2UtZGFyay10aGVtZScpOwp9OwoKZnVuY3Rpb24gbWVybWFpZEluaXQoKSB7CglpZiAobWVybWFpZFJlYWR5KCkpIHsKCQljb25zdCBtZXJtYWlkVGFyZ2V0Tm9kZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCdtZXJtYWlkJyk7CgoJCXRyeSB7CgkJCWNvbnN0IGRhcmtNb2RlID0gaXNEYXJrTW9kZSgpOwoJCQltZXJtYWlkLmluaXRpYWxpemUoewoJCQkJLy8gV2UgY2FsbCBtZXJtYWlkLnJ1biBvdXJzZWx2ZXMgd2hlbmV2ZXIgdGhlIG5vdGUgdXBkYXRlcy4gRG9uJ3QgYXV0by1zdGFydAoJCQkJc3RhcnRPbkxvYWQ6IGZhbHNlLAoKCQkJCWRhcmtNb2RlLAoJCQkJdGhlbWU6IGRhcmtNb2RlID8gJ2RhcmsnIDogJ2RlZmF1bHQnLAoJCQl9KTsKCQkJbWVybWFpZC5ydW4oewoJCQkJbm9kZXM6IG1lcm1haWRUYXJnZXROb2RlcywKCQkJfSk7CgkJfSBjYXRjaCAoZXJyb3IpIHsKCQkJY29uc29sZS5lcnJvcignTWVybWFpZCBlcnJvcicsIGVycm9yKTsKCQl9CgoJCS8vIFJlc2V0dGluZyBlbGVtZW50cyBzaXplIC0gc2VlIG1lcm1haWQudHMKCQlmb3IgKGNvbnN0IGVsZW1lbnQgb2YgbWVybWFpZFRhcmdldE5vZGVzKSB7CgkJCWVsZW1lbnQuc3R5bGUud2lkdGggPSAnMTAwJSc7CgkJfQoJfQp9Cgpkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdqb3BsaW4tbm90ZURpZFVwZGF0ZScsICgpID0+IHsKCW1lcm1haWRJbml0KCk7Cn0pOwoKY29uc3QgaW5pdElJRF8gPSBzZXRJbnRlcnZhbCgoKSA9PiB7Cgljb25zdCBpc1JlYWR5ID0gbWVybWFpZFJlYWR5KCk7CglpZiAoaXNSZWFkeSkgewoJCWNsZWFySW50ZXJ2YWwoaW5pdElJRF8pOwoJCW1lcm1haWRJbml0KCk7Cgl9Cn0sIDEwMCk7Cgpkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4gewoJLy8gSW4gc29tZSBlbnZpcm9ubWVudHMsIHdlIGNhbiBsb2FkIE1lcm1haWQgaW1tZWRpYXRlbHkgKGUuZy4gbW9iaWxlKS4KCS8vIElmIHdlIGRvbid0IGxvYWQgaXQgaW1tZWRpYXRlbHkgaW4gdGhlc2UgZW52aXJvbm1lbnRzLCBNZXJtYWlkIHNlZW1zCgkvLyB0byBpbml0aWFsaXplIGFuZCBhdXRvLXJ1biwgYnV0IHdpdGhvdXQgb3VyIGNvbmZpZ3VyYXRpb24gY2hhbmdlcy4KCWlmIChtZXJtYWlkUmVhZHkoKSkgewoJCW1lcm1haWRJbml0KCk7Cgl9IGVsc2UgewoJCWNsZWFySW50ZXJ2YWwoaW5pdElJRF8pOwoJfQp9KTsK`;
|
||||
module.exports = `LyogZ2xvYmFsIG1lcm1haWQgKi8KCmZ1bmN0aW9uIG1lcm1haWRSZWFkeSgpIHsKCS8vIFRoZSBNZXJtYWlkIGluaXRpYWxpemF0aW9uIGNvZGUgcmVuZGVycyB0aGUgTWVybWFpZCBjb2RlIHdpdGhpbiBhbnkgZWxlbWVudCB3aXRoIGNsYXNzICJtZXJtYWlkIiBvcgoJLy8gSUQgIm1lcm1haWQiLiBIb3dldmVyIGluIHNvbWUgY2FzZXMgc29tZSBlbGVtZW50cyBtaWdodCBoYXZlIHRoaXMgSUQgYnV0IG5vdCBiZSBNZXJtYWlkIGNvZGUuCgkvLyBGb3IgZXhhbXBsZSwgTWFya2Rvd24gY29kZSBsaWtlIHRoaXM6CgkvLwoJLy8gICAgICMgTWVybWFpZAoJLy8KCS8vIFdpbGwgZ2VuZXJhdGUgdGhpcyBIVE1MOgoJLy8KCS8vICAgICA8aDEgaWQ9Im1lcm1haWQiPk1lcm1haWQ8L2gxPgoJLy8KCS8vIEFuZCB0aGF0J3MgZ29pbmcgdG8gbWFrZSB0aGUgbGliIHNldCB0aGUgYG1lcm1haWRgIG9iamVjdCB0byB0aGUgSDEgZWxlbWVudC4KCS8vIFNvIGJlbG93LCB3ZSBkb3VibGUtY2hlY2sgdGhhdCB3aGF0IHdlIGhhdmUgcmVhbGx5IGlzIGFuIGluc3RhbmNlIG9mIHRoZSBsaWJyYXJ5LgoJcmV0dXJuIHR5cGVvZiBtZXJtYWlkICE9PSAndW5kZWZpbmVkJyAmJiBtZXJtYWlkICE9PSBudWxsICYmIHR5cGVvZiBtZXJtYWlkID09PSAnb2JqZWN0JyAmJiAhIW1lcm1haWQuaW5pdGlhbGl6ZTsKfQoKY29uc3QgaXNEYXJrTW9kZSA9ICgpID0+IHsKCS8vIElmIGFueSBtZXJtYWlkIGVsZW1lbnRzIGFyZSBtYXJrZWQgYXMgcmVxdWlyaW5nIGRhcmsgbW9kZSwgcmVuZGVyICphbGwqCgkvLyBtZXJtYWlkIGVsZW1lbnRzIGluIGRhcmsgbW9kZS4KCXJldHVybiAhIWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5tZXJtYWlkLmpvcGxpbi0tbWVybWFpZC11c2UtZGFyay10aGVtZScpOwp9OwoKY29uc3QgaW5pdEV4cG9ydEJ1dHRvbnMgPSAoKSA9PiB7Cgljb25zdCBleHBvcnRCdXR0b25zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLm1lcm1haWQtZXhwb3J0LWdyYXBoID4gYnV0dG9uJyk7Cglmb3IgKGNvbnN0IGJ1dHRvbiBvZiBleHBvcnRCdXR0b25zKSB7CgkJYnV0dG9uLm9uY2xpY2sgPSAoKSA9PiB7CgkJCWNvbnN0IGJ1dHRvbkNvbnRhaW5lciA9IGJ1dHRvbi5wYXJlbnRFbGVtZW50OwoJCQljb25zdCBtZXJtYWlkRWxlbSA9IGJ1dHRvbkNvbnRhaW5lci5uZXh0RWxlbWVudFNpYmxpbmc7CgoJCQljb25zdCByaWdodENsaWNrRXZlbnQgPSBuZXcgUG9pbnRlckV2ZW50KCdjb250ZXh0bWVudScsIHtidWJibGVzOiB0cnVlfSk7CgkJCXJpZ2h0Q2xpY2tFdmVudC50YXJnZXQgPSBtZXJtYWlkRWxlbTsKCQkJbWVybWFpZEVsZW0uZGlzcGF0Y2hFdmVudChyaWdodENsaWNrRXZlbnQpOwoJCX07Cgl9Cn07CgpmdW5jdGlvbiBtZXJtYWlkSW5pdCgpIHsKCWlmIChtZXJtYWlkUmVhZHkoKSkgewoJCWNvbnN0IG1lcm1haWRUYXJnZXROb2RlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ21lcm1haWQnKTsKCgkJdHJ5IHsKCQkJY29uc3QgZGFya01vZGUgPSBpc0RhcmtNb2RlKCk7CgkJCW1lcm1haWQuaW5pdGlhbGl6ZSh7CgkJCQkvLyBXZSBjYWxsIG1lcm1haWQucnVuIG91cnNlbHZlcyB3aGVuZXZlciB0aGUgbm90ZSB1cGRhdGVzLiBEb24ndCBhdXRvLXN0YXJ0CgkJCQlzdGFydE9uTG9hZDogZmFsc2UsCgoJCQkJZGFya01vZGUsCgkJCQl0aGVtZTogZGFya01vZGUgPyAnZGFyaycgOiAnZGVmYXVsdCcsCgkJCX0pOwoJCQltZXJtYWlkLnJ1bih7CgkJCQlub2RlczogbWVybWFpZFRhcmdldE5vZGVzLAoJCQl9KTsKCQl9IGNhdGNoIChlcnJvcikgewoJCQljb25zb2xlLmVycm9yKCdNZXJtYWlkIGVycm9yJywgZXJyb3IpOwoJCX0KCgkJLy8gUmVzZXR0aW5nIGVsZW1lbnRzIHNpemUgLSBzZWUgbWVybWFpZC50cwoJCWZvciAoY29uc3QgZWxlbWVudCBvZiBtZXJtYWlkVGFyZ2V0Tm9kZXMpIHsKCQkJZWxlbWVudC5zdHlsZS53aWR0aCA9ICcxMDAlJzsKCQl9CgoJCWluaXRFeHBvcnRCdXR0b25zKCk7Cgl9Cn0KCmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2pvcGxpbi1ub3RlRGlkVXBkYXRlJywgKCkgPT4gewoJbWVybWFpZEluaXQoKTsKfSk7Cgpjb25zdCBpbml0SUlEXyA9IHNldEludGVydmFsKCgpID0+IHsKCWNvbnN0IGlzUmVhZHkgPSBtZXJtYWlkUmVhZHkoKTsKCWlmIChpc1JlYWR5KSB7CgkJY2xlYXJJbnRlcnZhbChpbml0SUlEXyk7CgkJbWVybWFpZEluaXQoKTsKCX0KfSwgMTAwKTsKCmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7CgkvLyBJbiBzb21lIGVudmlyb25tZW50cywgd2UgY2FuIGxvYWQgTWVybWFpZCBpbW1lZGlhdGVseSAoZS5nLiBtb2JpbGUpLgoJLy8gSWYgd2UgZG9uJ3QgbG9hZCBpdCBpbW1lZGlhdGVseSBpbiB0aGVzZSBlbnZpcm9ubWVudHMsIE1lcm1haWQgc2VlbXMKCS8vIHRvIGluaXRpYWxpemUgYW5kIGF1dG8tcnVuLCBidXQgd2l0aG91dCBvdXIgY29uZmlndXJhdGlvbiBjaGFuZ2VzLgoJaWYgKG1lcm1haWRSZWFkeSgpKSB7CgkJbWVybWFpZEluaXQoKTsKCX0gZWxzZSB7CgkJY2xlYXJJbnRlcnZhbChpbml0SUlEXyk7Cgl9Cn0pOwo=`;
|
||||
@@ -1727,6 +1727,17 @@ const builtInMetadata = (Setting: typeof SettingType) => {
|
||||
isGlobal: true,
|
||||
},
|
||||
|
||||
'featureFlag.richText.useStrictContentSecurityPolicy': {
|
||||
value: true,
|
||||
type: SettingItemType.Bool,
|
||||
public: true,
|
||||
storage: SettingStorage.File,
|
||||
label: () => 'Stronger security controls in the Rich Text Editor',
|
||||
description: () => 'Improves Rich Text Editor security by applying a strict content security policy to the Rich Text Editor\'s content.',
|
||||
section: 'note',
|
||||
isGlobal: true,
|
||||
},
|
||||
|
||||
'sync.allowUnsupportedProviders': {
|
||||
value: -1,
|
||||
type: SettingItemType.Int,
|
||||
|
||||
@@ -399,9 +399,10 @@ export default class MdToHtml implements MarkupRenderer {
|
||||
public async allAssets(theme: any, noteStyleOptions: NoteStyleOptions = null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const assets: any = {};
|
||||
for (const key in rules) {
|
||||
const allRules = { ...rules, ...this.extraRendererRules_ };
|
||||
for (const key in allRules) {
|
||||
if (!this.pluginEnabled(key)) continue;
|
||||
const rule = rules[key];
|
||||
const rule = allRules[key];
|
||||
|
||||
if (rule.assets) {
|
||||
assets[key] = rule.assets(theme);
|
||||
|
||||
@@ -98,17 +98,6 @@ export default {
|
||||
|
||||
const exportGraphButton = (ruleOptions: RuleOptions) => {
|
||||
const theme = ruleOptions.theme;
|
||||
// Clicking on export button manually triggers a right click context menu event
|
||||
const onClickHandler = `
|
||||
const target = arguments[0].target;
|
||||
const button = target.closest("div.mermaid-export-graph");
|
||||
if (!button) return false;
|
||||
const $mermaid_elem = button.nextElementSibling;
|
||||
const rightClickEvent = new PointerEvent("contextmenu", {bubbles: true});
|
||||
rightClickEvent.target = $mermaid_elem;
|
||||
$mermaid_elem.dispatchEvent(rightClickEvent);
|
||||
return false;
|
||||
`.trim();
|
||||
const style = `
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
@@ -119,9 +108,10 @@ const exportGraphButton = (ruleOptions: RuleOptions) => {
|
||||
border: ${theme.buttonStyle.border};
|
||||
`.trim();
|
||||
|
||||
// OnClick is handled in the renderer script
|
||||
return `
|
||||
<div class="mermaid-export-graph">
|
||||
<button onclick='${onClickHandler}' style="${style}" alt="Export mermaid graph">${downloadIcon()}</button>
|
||||
<button style="${style}" alt="Export mermaid graph">${downloadIcon()}</button>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,20 @@ const isDarkMode = () => {
|
||||
return !!document.querySelector('.mermaid.joplin--mermaid-use-dark-theme');
|
||||
};
|
||||
|
||||
const initExportButtons = () => {
|
||||
const exportButtons = document.querySelectorAll('.mermaid-export-graph > button');
|
||||
for (const button of exportButtons) {
|
||||
button.onclick = () => {
|
||||
const buttonContainer = button.parentElement;
|
||||
const mermaidElem = buttonContainer.nextElementSibling;
|
||||
|
||||
const rightClickEvent = new PointerEvent('contextmenu', { bubbles: true });
|
||||
rightClickEvent.target = mermaidElem;
|
||||
mermaidElem.dispatchEvent(rightClickEvent);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function mermaidInit() {
|
||||
if (mermaidReady()) {
|
||||
const mermaidTargetNodes = document.getElementsByClassName('mermaid');
|
||||
@@ -46,6 +60,8 @@ function mermaidInit() {
|
||||
for (const element of mermaidTargetNodes) {
|
||||
element.style.width = '100%';
|
||||
}
|
||||
|
||||
initExportButtons();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,20 @@ const isDarkMode = () => {
|
||||
return !!document.querySelector('.mermaid.joplin--mermaid-use-dark-theme');
|
||||
};
|
||||
|
||||
const initExportButtons = () => {
|
||||
const exportButtons = document.querySelectorAll('.mermaid-export-graph > button');
|
||||
for (const button of exportButtons) {
|
||||
button.onclick = () => {
|
||||
const buttonContainer = button.parentElement;
|
||||
const mermaidElem = buttonContainer.nextElementSibling;
|
||||
|
||||
const rightClickEvent = new PointerEvent('contextmenu', {bubbles: true});
|
||||
rightClickEvent.target = mermaidElem;
|
||||
mermaidElem.dispatchEvent(rightClickEvent);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function mermaidInit() {
|
||||
if (mermaidReady()) {
|
||||
const mermaidTargetNodes = document.getElementsByClassName('mermaid');
|
||||
@@ -46,6 +60,8 @@ function mermaidInit() {
|
||||
for (const element of mermaidTargetNodes) {
|
||||
element.style.width = '100%';
|
||||
}
|
||||
|
||||
initExportButtons();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user