mirror of
https://github.com/tinymce/tinymce.git
synced 2025-08-16 18:21:47 +00:00
TINY-11755: Retain semantics if annotations are allowed (#10122)
Related Ticket: TINY-11755 Description of Changes: * Fixed the issue with `semantics` being removed even when `annotation` elements was allowed * Also changed so that the `annotation` element is properly removed if it's not allowed so that it wouldn't get rendered. * The `semantics` element is unwrapped if it's not allowed that should work since the first one should be equation and we want to keep that. Pre-checks: * [x] Changelog entry added * [x] Tests have been added (if applicable) * [x] Branch prefixed with `feature/`, `hotfix/` or `spike/` Review: * [x] Milestone set * [x] Docs ticket created (if applicable) GitHub issues (if applicable): <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Resolved an issue with MathML element handling, specifically ensuring the `semantics` element is properly retained when annotation elements are allowed. - Improved parsing and sanitization of MathML annotations based on specified encoding rules. - **Tests** - Added new test cases to verify MathML annotation handling under different configuration scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
7
.changes/unreleased/tinymce-TINY-11755-2025-01-29.yaml
Normal file
7
.changes/unreleased/tinymce-TINY-11755-2025-01-29.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
project: tinymce
|
||||
kind: Fixed
|
||||
body: The `semantics` element in MathML was not properly retained when `annotation`
|
||||
elements was allowed.
|
||||
time: 2025-01-29T08:43:41.042875+01:00
|
||||
custom:
|
||||
Issue: TINY-11755
|
@ -232,15 +232,25 @@ const sanitizeMathmlElement = (node: Element, settings: DomParserSettings) => {
|
||||
};
|
||||
|
||||
const purify = createDompurify();
|
||||
const allowedEncodings = settings.allow_mathml_annotation_encodings;
|
||||
const hasAllowedEncodings = Type.isArray(allowedEncodings) && allowedEncodings.length > 0;
|
||||
const hasValidEncoding = (el: Element) => {
|
||||
const encoding = el.getAttribute('encoding');
|
||||
return hasAllowedEncodings && Type.isString(encoding) && Arr.contains(allowedEncodings, encoding);
|
||||
};
|
||||
|
||||
purify.addHook('uponSanitizeElement', (node, evt) => {
|
||||
const lcTagName = evt.tagName ?? node.nodeName.toLowerCase();
|
||||
const allowedEncodings = settings.allow_mathml_annotation_encodings;
|
||||
|
||||
if (lcTagName === 'annotation' && Type.isArray(allowedEncodings) && allowedEncodings.length > 0) {
|
||||
const encoding = node.getAttribute('encoding');
|
||||
if (Type.isString(encoding) && Arr.contains(allowedEncodings, encoding)) {
|
||||
evt.allowedTags[lcTagName] = true;
|
||||
if (hasAllowedEncodings && lcTagName === 'semantics') {
|
||||
evt.allowedTags[lcTagName] = true;
|
||||
}
|
||||
|
||||
if (lcTagName === 'annotation') {
|
||||
const keepElement = hasValidEncoding(node);
|
||||
evt.allowedTags[lcTagName] = keepElement;
|
||||
if (!keepElement) {
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -724,45 +724,83 @@ describe('browser.tinymce.core.content.EditorContentTest', () => {
|
||||
});
|
||||
|
||||
context('math elements', () => {
|
||||
const hook = TinyHooks.bddSetupLight<Editor>({
|
||||
base_url: '/project/tinymce/js/tinymce',
|
||||
custom_elements: 'math',
|
||||
allow_mathml_annotation_encodings: [
|
||||
'application/x-tex',
|
||||
'application/custom',
|
||||
'wiris'
|
||||
]
|
||||
}, []);
|
||||
context('annotations encodings defined', () => {
|
||||
const hook = TinyHooks.bddSetupLight<Editor>({
|
||||
base_url: '/project/tinymce/js/tinymce',
|
||||
custom_elements: 'math',
|
||||
allow_mathml_annotation_encodings: [
|
||||
'application/x-tex',
|
||||
'application/custom',
|
||||
'wiris'
|
||||
]
|
||||
}, []);
|
||||
|
||||
it('TINY-11166: allow_mathml_annotation_encodings should retain the specified annotation elements', () => {
|
||||
const editor = hook.editor();
|
||||
it('TINY-11166: allow_mathml_annotation_encodings should retain the specified annotation elements and the parent semantics element', () => {
|
||||
const editor = hook.editor();
|
||||
|
||||
const input = [
|
||||
'<div>',
|
||||
'<math><annotation encoding="application/x-tex">\\frac{1}{2}</annotation></math>',
|
||||
'<math><annotation encoding="application/custom">custom</annotation></math>',
|
||||
'<math><annotation encoding="application/custom" src="foo">custom with src</annotation></math>',
|
||||
'<math><annotation encoding="wiris">{"version":"1.1","math":"<math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mn>2</mn></mfrac></math>"}</annotation></math>',
|
||||
'<math><annotation encoding="text/html">html</annotation></math>',
|
||||
'<math><annotation encoding="text/svg">svg</annotation></math>',
|
||||
'</div>'
|
||||
].join('');
|
||||
const input = [
|
||||
'<div>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/x-tex">\\frac{1}{2}</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/custom">custom</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/custom" src="foo">custom with src</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="wiris">{"version":"1.1","math":"<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1></mn></math>"}</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="text/html">html</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="text/svg">svg</annotation></semantics></math>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
editor.setContent(input);
|
||||
editor.setContent(input);
|
||||
|
||||
const expected = [
|
||||
'<div>',
|
||||
'<math><annotation encoding="application/x-tex">\\frac{1}{2}</annotation></math>',
|
||||
'<math><annotation encoding="application/custom">custom</annotation></math>',
|
||||
'<math><annotation encoding="application/custom">custom with src</annotation></math>',
|
||||
'<math><annotation encoding="wiris">{"version":"1.1","math":"<math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mn>2</mn></mfrac></math>"}</annotation></math>',
|
||||
'<math>html</math>',
|
||||
'<math>svg</math>',
|
||||
'</div>'
|
||||
].join('');
|
||||
const expected = [
|
||||
'<div>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/x-tex">\\frac{1}{2}</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/custom">custom</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/custom">custom with src</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="wiris">{"version":"1.1","math":"<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1></mn></math>"}</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn></semantics></math>',
|
||||
'<math><semantics><mn>1</mn></semantics></math>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
TinyAssertions.assertContent(editor, expected);
|
||||
TinyAssertions.assertContent(editor, expected);
|
||||
});
|
||||
});
|
||||
|
||||
context('annotations encodings not defined', () => {
|
||||
const hook = TinyHooks.bddSetupLight<Editor>({
|
||||
base_url: '/project/tinymce/js/tinymce',
|
||||
custom_elements: 'math'
|
||||
}, []);
|
||||
|
||||
it('TINY-11166: not setting allow_mathml_annotation_encodings should not retain the semantics or annotation elements', () => {
|
||||
const editor = hook.editor();
|
||||
|
||||
const input = [
|
||||
'<div>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/x-tex">\\frac{1}{2}</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/custom">custom</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="application/custom" src="foo">custom with src</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="wiris">{"version":"1.1","math":"<math xmlns="http://www.w3.org/1998/Math/MathML"><mn>1></mn></math>"}</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="text/html">html</annotation></semantics></math>',
|
||||
'<math><semantics><mn>1</mn><annotation encoding="text/svg">svg</annotation></semantics></math>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
editor.setContent(input);
|
||||
|
||||
const expected = [
|
||||
'<div>',
|
||||
'<math><mn>1</mn></math>',
|
||||
'<math><mn>1</mn></math>',
|
||||
'<math><mn>1</mn></math>',
|
||||
'<math><mn>1</mn></math>',
|
||||
'<math><mn>1</mn></math>',
|
||||
'<math><mn>1</mn></math>',
|
||||
'</div>'
|
||||
].join('');
|
||||
|
||||
TinyAssertions.assertContent(editor, expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1748,6 +1748,13 @@ describe('browser.tinymce.core.html.DomParserTest', () => {
|
||||
assert.equal(serializedHtml, '<div><math> <mrow> </mrow> </math> <math> </math></div>');
|
||||
});
|
||||
|
||||
it('TINY-11755: Should retain semantics and annotations if allow_mathml_annotation_encodings is set', () => {
|
||||
const schema = Schema();
|
||||
schema.addValidElements('math[*]');
|
||||
const input = '<math><semantics><annotation encoding="-x-custom-mime">annotation1</annotation><annotation encoding="text/html">annotation2</annotation></semantics></math>';
|
||||
const serializedHtml = HtmlSerializer({}, schema).serialize(DomParser({ allow_mathml_annotation_encodings: [ '-x-custom-mime' ] }, schema).parse(input));
|
||||
assert.equal(serializedHtml, '<math><semantics><annotation encoding="-x-custom-mime">annotation1</annotation></semantics></math>');
|
||||
});
|
||||
});
|
||||
|
||||
context('Special elements', () => {
|
||||
|
Reference in New Issue
Block a user