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];
|
const token = tokens[idx];
|
||||||
if (token.info !== 'justtesting') return defaultRender(tokens, idx, options, env, self);
|
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:
|
// Rich text editor support:
|
||||||
// The joplin-editable and joplin-source CSS classes mark the generated div
|
// The joplin-editable and joplin-source CSS classes mark the generated div
|
||||||
// as a region that needs special processing when converting back to markdown.
|
// as a region that needs special processing when converting back to markdown.
|
||||||
@@ -38,14 +31,23 @@ export default function(context) {
|
|||||||
${richTextEditorMetadata}
|
${richTextEditorMetadata}
|
||||||
|
|
||||||
<p>JUST TESTING: <pre>${markdownIt.utils.escapeHtml(leftPad(token.content.trim(), 10, 'x'))}</pre></p>
|
<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>
|
</div>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
assets: function() {
|
assets: function() {
|
||||||
return [
|
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 useDocument from '../../../hooks/useDocument';
|
||||||
import useEditDialog from './utils/useEditDialog';
|
import useEditDialog from './utils/useEditDialog';
|
||||||
import useEditDialogEventListeners from './utils/useEditDialogEventListeners';
|
import useEditDialogEventListeners from './utils/useEditDialogEventListeners';
|
||||||
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
import useTextPatternsLookup from './utils/useTextPatternsLookup';
|
import useTextPatternsLookup from './utils/useTextPatternsLookup';
|
||||||
|
|
||||||
const logger = Logger.create('TinyMCE');
|
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}`,
|
language_url: ['en_US', 'en_GB'].includes(language) ? undefined : `${bridge().vendorDir()}/lib/tinymce/langs/${language}`,
|
||||||
toolbar: toolbar.join(' '),
|
toolbar: toolbar.join(' '),
|
||||||
localization_function: _,
|
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,
|
contextmenu: false,
|
||||||
browser_spellcheck: true,
|
browser_spellcheck: true,
|
||||||
|
|
||||||
|
|||||||
@@ -2,72 +2,37 @@ import PluginService from '@joplin/lib/services/plugins/PluginService';
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Editor } from 'tinymce';
|
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(() => {
|
useEffect(() => {
|
||||||
if (!editor) return ()=>{};
|
if (!editor) return ()=>{};
|
||||||
if (!window) return ()=>{};
|
if (!containerWindow) return ()=>{};
|
||||||
|
|
||||||
const scriptElement = window.document.createElement('script');
|
const editorWindow = editor.getWin() as ExtendedWindow;
|
||||||
const channelId = `plugin-post-message-${Math.random()}`;
|
const webviewApi: WebViewApi = {
|
||||||
scriptElement.appendChild(window.document.createTextNode(`
|
postMessage: async (contentScriptId: string, message: unknown) => {
|
||||||
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 pluginService = PluginService.instance();
|
const pluginService = PluginService.instance();
|
||||||
const plugin = pluginService.pluginById(
|
const plugin = pluginService.pluginById(
|
||||||
pluginService.pluginIdByContentScriptId(contentScriptId),
|
pluginService.pluginIdByContentScriptId(contentScriptId),
|
||||||
);
|
);
|
||||||
const result = await plugin.emitContentScriptMessage(contentScriptId, event.data.message);
|
return await plugin.emitContentScriptMessage(contentScriptId, message);
|
||||||
editorWindow.postMessage({
|
},
|
||||||
messageId: event.data.messageId,
|
|
||||||
response: result,
|
|
||||||
}, '*');
|
|
||||||
};
|
};
|
||||||
window.addEventListener('message', onMessageHandler);
|
editorWindow.webviewApi = webviewApi;
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('message', onMessageHandler);
|
if (editorWindow.webviewApi === webviewApi) {
|
||||||
scriptElement.remove();
|
editorWindow.webviewApi = undefined;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [editor, window]);
|
}, [editor, containerWindow]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useWebViewApi;
|
export default useWebViewApi;
|
||||||
|
|||||||
@@ -163,8 +163,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
|||||||
scrollbarSize: props.scrollbarSize,
|
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) => {
|
||||||
const allAssets = useCallback(async (markupLanguage: number, options: AllAssetsOptions = null): Promise<any[]> => {
|
|
||||||
options = {
|
options = {
|
||||||
contentMaxWidthTarget: '',
|
contentMaxWidthTarget: '',
|
||||||
...options,
|
...options,
|
||||||
@@ -172,7 +171,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
|||||||
|
|
||||||
const theme = themeStyle(options.themeId ? options.themeId : props.themeId);
|
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')}/`,
|
resourceBaseUrl: `joplin-content://note-viewer/${Setting.value('resourceDir')}/`,
|
||||||
customCss: props.customCss,
|
customCss: props.customCss,
|
||||||
});
|
});
|
||||||
@@ -183,7 +182,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
|||||||
scrollbarSize: props.scrollbarSize,
|
scrollbarSize: props.scrollbarSize,
|
||||||
whiteBackgroundNoteRendering: options.whiteBackgroundNoteRendering,
|
whiteBackgroundNoteRendering: options.whiteBackgroundNoteRendering,
|
||||||
});
|
});
|
||||||
}, [props.themeId, props.scrollbarSize, props.customCss, props.contentMaxWidth]);
|
}, [props.plugins, props.themeId, props.scrollbarSize, props.customCss, props.contentMaxWidth]);
|
||||||
|
|
||||||
const handleProvisionalFlag = useCallback(() => {
|
const handleProvisionalFlag = useCallback(() => {
|
||||||
if (props.isProvisional) {
|
if (props.isProvisional) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
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-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' },
|
'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' },
|
'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,
|
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': {
|
'sync.allowUnsupportedProviders': {
|
||||||
value: -1,
|
value: -1,
|
||||||
type: SettingItemType.Int,
|
type: SettingItemType.Int,
|
||||||
|
|||||||
@@ -399,9 +399,10 @@ export default class MdToHtml implements MarkupRenderer {
|
|||||||
public async allAssets(theme: any, noteStyleOptions: NoteStyleOptions = null) {
|
public async allAssets(theme: any, noteStyleOptions: NoteStyleOptions = null) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
const assets: any = {};
|
const assets: any = {};
|
||||||
for (const key in rules) {
|
const allRules = { ...rules, ...this.extraRendererRules_ };
|
||||||
|
for (const key in allRules) {
|
||||||
if (!this.pluginEnabled(key)) continue;
|
if (!this.pluginEnabled(key)) continue;
|
||||||
const rule = rules[key];
|
const rule = allRules[key];
|
||||||
|
|
||||||
if (rule.assets) {
|
if (rule.assets) {
|
||||||
assets[key] = rule.assets(theme);
|
assets[key] = rule.assets(theme);
|
||||||
|
|||||||
@@ -98,17 +98,6 @@ export default {
|
|||||||
|
|
||||||
const exportGraphButton = (ruleOptions: RuleOptions) => {
|
const exportGraphButton = (ruleOptions: RuleOptions) => {
|
||||||
const theme = ruleOptions.theme;
|
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 = `
|
const style = `
|
||||||
display: block;
|
display: block;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
@@ -119,9 +108,10 @@ const exportGraphButton = (ruleOptions: RuleOptions) => {
|
|||||||
border: ${theme.buttonStyle.border};
|
border: ${theme.buttonStyle.border};
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
|
// OnClick is handled in the renderer script
|
||||||
return `
|
return `
|
||||||
<div class="mermaid-export-graph">
|
<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>
|
</div>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,20 @@ const isDarkMode = () => {
|
|||||||
return !!document.querySelector('.mermaid.joplin--mermaid-use-dark-theme');
|
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() {
|
function mermaidInit() {
|
||||||
if (mermaidReady()) {
|
if (mermaidReady()) {
|
||||||
const mermaidTargetNodes = document.getElementsByClassName('mermaid');
|
const mermaidTargetNodes = document.getElementsByClassName('mermaid');
|
||||||
@@ -46,6 +60,8 @@ function mermaidInit() {
|
|||||||
for (const element of mermaidTargetNodes) {
|
for (const element of mermaidTargetNodes) {
|
||||||
element.style.width = '100%';
|
element.style.width = '100%';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initExportButtons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,20 @@ const isDarkMode = () => {
|
|||||||
return !!document.querySelector('.mermaid.joplin--mermaid-use-dark-theme');
|
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() {
|
function mermaidInit() {
|
||||||
if (mermaidReady()) {
|
if (mermaidReady()) {
|
||||||
const mermaidTargetNodes = document.getElementsByClassName('mermaid');
|
const mermaidTargetNodes = document.getElementsByClassName('mermaid');
|
||||||
@@ -46,6 +60,8 @@ function mermaidInit() {
|
|||||||
for (const element of mermaidTargetNodes) {
|
for (const element of mermaidTargetNodes) {
|
||||||
element.style.width = '100%';
|
element.style.width = '100%';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initExportButtons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user