diff --git a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts
index 60a76d9567c6a..ce86852dca7d7 100644
--- a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts
+++ b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts
@@ -3507,6 +3507,32 @@ runInEachFileSystem(() => {
1. If 'foo' is an Angular component, then verify that it is part of this module.
2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`);
});
+
+ it('should allow math elements', () => {
+ env.write(
+ 'test.ts',
+ `
+ import {Component} from '@angular/core';
+ @Component({
+ template: \`
+
+ \`,
+ standalone: true,
+ })
+ export class MathCmp {}
+ `,
+ );
+
+ const diags = env.driveDiagnostics();
+ expect(diags.length).toBe(0);
+ });
});
// Test both sync and async compilations, see https://github.com/angular/angular/issues/32538
diff --git a/packages/compiler/src/schema/dom_element_schema_registry.ts b/packages/compiler/src/schema/dom_element_schema_registry.ts
index e5b98ef1cbf81..e5ac5b9abf8f5 100644
--- a/packages/compiler/src/schema/dom_element_schema_registry.ts
+++ b/packages/compiler/src/schema/dom_element_schema_registry.ts
@@ -229,6 +229,36 @@ const SCHEMA: string[] = [
'summary^[HTMLElement]|',
'time^[HTMLElement]|dateTime',
':svg:cursor^:svg:|',
+ ':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',
+ ':math:math^:math:|',
+ ':math:maction^:math:|',
+ ':math:menclose^:math:|',
+ ':math:merror^:math:|',
+ ':math:mfenced^:math:|',
+ ':math:mfrac^:math:|',
+ ':math:mi^:math:|',
+ ':math:mmultiscripts^:math:|',
+ ':math:mn^:math:|',
+ ':math:mo^:math:|',
+ ':math:mover^:math:|',
+ ':math:mpadded^:math:|',
+ ':math:mphantom^:math:|',
+ ':math:mroot^:math:|',
+ ':math:mrow^:math:|',
+ ':math:ms^:math:|',
+ ':math:mspace^:math:|',
+ ':math:msqrt^:math:|',
+ ':math:mstyle^:math:|',
+ ':math:msub^:math:|',
+ ':math:msubsup^:math:|',
+ ':math:msup^:math:|',
+ ':math:mtable^:math:|',
+ ':math:mtd^:math:|',
+ ':math:mtext^:math:|',
+ ':math:mtr^:math:|',
+ ':math:munder^:math:|',
+ ':math:munderover^:math:|',
+ ':math:semantics^:math:|',
];
const _ATTR_TO_PROP = new Map(
diff --git a/packages/compiler/test/schema/schema_extractor.ts b/packages/compiler/test/schema/schema_extractor.ts
index bd73d415ebce7..e5d4d7f164275 100644
--- a/packages/compiler/test/schema/schema_extractor.ts
+++ b/packages/compiler/test/schema/schema_extractor.ts
@@ -7,6 +7,7 @@
*/
const SVG_PREFIX = ':svg:';
+const MATH_PREFIX = ':math:';
// Element | Node interfaces
// see https://developer.mozilla.org/en-US/docs/Web/API/Element
@@ -25,6 +26,10 @@ const ALL_HTML_TAGS =
// https://html.spec.whatwg.org/
'details,summary,menu,menuitem';
+// Via https://developer.mozilla.org/en-US/docs/Web/MathML
+const ALL_MATH_TAGS =
+ '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';
+
// Elements missing from Chrome (HtmlUnknownElement), to be manually added
const MISSING_FROM_CHROME: {[el: string]: string[]} = {
'data^[HTMLElement]': ['value'],
@@ -81,8 +86,8 @@ export function extractSchema(): Map | null {
const SVGGradientElement = _G['SVGGradientElement'];
const SVGTextContentElement = _G['SVGTextContentElement'];
const SVGTextPositioningElement = _G['SVGTextPositioningElement'];
-
extractProperties(SVGElement, svgText, visited, descMap, SVG_PREFIX, HTMLELEMENT_IF);
+
extractProperties(
SVGGraphicsElement,
svgText,
@@ -154,6 +159,22 @@ export function extractSchema(): Map | null {
descMap.set(elHierarchy, MISSING_FROM_CHROME[elHierarchy]);
});
+ // Needed because we're running tests against some older Android versions.
+ if (typeof MathMLElement !== 'undefined') {
+ // Math top level
+ const math = document.createElementNS('http://www.w3.org/1998/Math/MathML', 'math');
+ extractProperties(MathMLElement, math, visited, descMap, MATH_PREFIX, HTMLELEMENT_IF);
+
+ // This script is written under the assumption that each tag has a corresponding class name, e.g.
+ // `` -> `SVGCircleElement` however this doesn't hold for Math elements which are all
+ // `MathMLElement`. Furthermore, they don't have special property names, but rather are
+ // configured exclusively via attributes. Register them as plain elements that inherit from
+ // the top-level `:math` namespace.
+ ALL_MATH_TAGS.split(',').forEach((tag) =>
+ descMap.set(`${MATH_PREFIX}${tag}^${MATH_PREFIX}`, []),
+ );
+ }
+
assertNoMissingTags(descMap);
return descMap;
@@ -166,7 +187,12 @@ function assertNoMissingTags(descMap: Map): void {
extractedTags.push(...key.split('|')[0].split('^')[0].split(','));
});
- const missingTags = ALL_HTML_TAGS.split(',').filter((tag) => extractedTags.indexOf(tag) == -1);
+ const missingTags = [
+ ...ALL_HTML_TAGS.split(','),
+ ...(typeof MathMLElement === 'undefined'
+ ? []
+ : ALL_MATH_TAGS.split(',').map((tag) => MATH_PREFIX + tag)),
+ ].filter((tag) => !extractedTags.includes(tag));
if (missingTags.length) {
throw new Error(`DOM schema misses tags: ${missingTags.join(',')}`);