Skip to content

Commit 91b007e

Browse files
crisbetoAndrewKushnir
authored andcommitted
fix(compiler): add math elements to schema (#55631)
Fixes that we didn't have the MathML elements in the schema. Note that we can't discover which tag names are available by looking at globally-available classes, because all MathML elements are `MathMLElement` rather than something like `SVGCircleElement`. As such, I ended up having to hardcode the currently-available tags. Fixes #55608. PR Close #55631
1 parent da5ef52 commit 91b007e

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3507,6 +3507,32 @@ runInEachFileSystem(() => {
35073507
1. If 'foo' is an Angular component, then verify that it is part of this module.
35083508
2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`);
35093509
});
3510+
3511+
it('should allow math elements', () => {
3512+
env.write(
3513+
'test.ts',
3514+
`
3515+
import {Component} from '@angular/core';
3516+
@Component({
3517+
template: \`
3518+
<math>
3519+
<mfrac>
3520+
<mn>1</mn>
3521+
<msqrt>
3522+
<mn>2</mn>
3523+
</msqrt>
3524+
</mfrac>
3525+
</math>
3526+
\`,
3527+
standalone: true,
3528+
})
3529+
export class MathCmp {}
3530+
`,
3531+
);
3532+
3533+
const diags = env.driveDiagnostics();
3534+
expect(diags.length).toBe(0);
3535+
});
35103536
});
35113537

35123538
// Test both sync and async compilations, see https://github.com/angular/angular/issues/32538

packages/compiler/src/schema/dom_element_schema_registry.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,36 @@ const SCHEMA: string[] = [
229229
'summary^[HTMLElement]|',
230230
'time^[HTMLElement]|dateTime',
231231
':svg:cursor^:svg:|',
232+
':math:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforeinput,*beforematch,*beforetoggle,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contentvisibilityautostatechange,*contextlost,*contextmenu,*contextrestored,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*scrollend,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex',
233+
':math:math^:math:|',
234+
':math:maction^:math:|',
235+
':math:menclose^:math:|',
236+
':math:merror^:math:|',
237+
':math:mfenced^:math:|',
238+
':math:mfrac^:math:|',
239+
':math:mi^:math:|',
240+
':math:mmultiscripts^:math:|',
241+
':math:mn^:math:|',
242+
':math:mo^:math:|',
243+
':math:mover^:math:|',
244+
':math:mpadded^:math:|',
245+
':math:mphantom^:math:|',
246+
':math:mroot^:math:|',
247+
':math:mrow^:math:|',
248+
':math:ms^:math:|',
249+
':math:mspace^:math:|',
250+
':math:msqrt^:math:|',
251+
':math:mstyle^:math:|',
252+
':math:msub^:math:|',
253+
':math:msubsup^:math:|',
254+
':math:msup^:math:|',
255+
':math:mtable^:math:|',
256+
':math:mtd^:math:|',
257+
':math:mtext^:math:|',
258+
':math:mtr^:math:|',
259+
':math:munder^:math:|',
260+
':math:munderover^:math:|',
261+
':math:semantics^:math:|',
232262
];
233263

234264
const _ATTR_TO_PROP = new Map(

packages/compiler/test/schema/schema_extractor.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
const SVG_PREFIX = ':svg:';
10+
const MATH_PREFIX = ':math:';
1011

1112
// Element | Node interfaces
1213
// see https://developer.mozilla.org/en-US/docs/Web/API/Element
@@ -25,6 +26,10 @@ const ALL_HTML_TAGS =
2526
// https://html.spec.whatwg.org/
2627
'details,summary,menu,menuitem';
2728

29+
// Via https://developer.mozilla.org/en-US/docs/Web/MathML
30+
const ALL_MATH_TAGS =
31+
'math,maction,menclose,merror,mfenced,mfrac,mi,mmultiscripts,mn,mo,mover,mpadded,mphantom,mroot,mrow,ms,mspace,msqrt,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,semantics';
32+
2833
// Elements missing from Chrome (HtmlUnknownElement), to be manually added
2934
const MISSING_FROM_CHROME: {[el: string]: string[]} = {
3035
'data^[HTMLElement]': ['value'],
@@ -81,8 +86,8 @@ export function extractSchema(): Map<string, string[]> | null {
8186
const SVGGradientElement = _G['SVGGradientElement'];
8287
const SVGTextContentElement = _G['SVGTextContentElement'];
8388
const SVGTextPositioningElement = _G['SVGTextPositioningElement'];
84-
8589
extractProperties(SVGElement, svgText, visited, descMap, SVG_PREFIX, HTMLELEMENT_IF);
90+
8691
extractProperties(
8792
SVGGraphicsElement,
8893
svgText,
@@ -154,6 +159,22 @@ export function extractSchema(): Map<string, string[]> | null {
154159
descMap.set(elHierarchy, MISSING_FROM_CHROME[elHierarchy]);
155160
});
156161

162+
// Needed because we're running tests against some older Android versions.
163+
if (typeof MathMLElement !== 'undefined') {
164+
// Math top level
165+
const math = document.createElementNS('http://www.w3.org/1998/Math/MathML', 'math');
166+
extractProperties(MathMLElement, math, visited, descMap, MATH_PREFIX, HTMLELEMENT_IF);
167+
168+
// This script is written under the assumption that each tag has a corresponding class name, e.g.
169+
// `<circle>` -> `SVGCircleElement` however this doesn't hold for Math elements which are all
170+
// `MathMLElement`. Furthermore, they don't have special property names, but rather are
171+
// configured exclusively via attributes. Register them as plain elements that inherit from
172+
// the top-level `:math` namespace.
173+
ALL_MATH_TAGS.split(',').forEach((tag) =>
174+
descMap.set(`${MATH_PREFIX}${tag}^${MATH_PREFIX}`, []),
175+
);
176+
}
177+
157178
assertNoMissingTags(descMap);
158179

159180
return descMap;
@@ -166,7 +187,12 @@ function assertNoMissingTags(descMap: Map<string, string[]>): void {
166187
extractedTags.push(...key.split('|')[0].split('^')[0].split(','));
167188
});
168189

169-
const missingTags = ALL_HTML_TAGS.split(',').filter((tag) => extractedTags.indexOf(tag) == -1);
190+
const missingTags = [
191+
...ALL_HTML_TAGS.split(','),
192+
...(typeof MathMLElement === 'undefined'
193+
? []
194+
: ALL_MATH_TAGS.split(',').map((tag) => MATH_PREFIX + tag)),
195+
].filter((tag) => !extractedTags.includes(tag));
170196

171197
if (missingTags.length) {
172198
throw new Error(`DOM schema misses tags: ${missingTags.join(',')}`);

0 commit comments

Comments
 (0)