Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: \`
<math>
<mfrac>
<mn>1</mn>
<msqrt>
<mn>2</mn>
</msqrt>
</mfrac>
</math>
\`,
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
Expand Down
30 changes: 30 additions & 0 deletions packages/compiler/src/schema/dom_element_schema_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
30 changes: 28 additions & 2 deletions packages/compiler/test/schema/schema_extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'],
Expand Down Expand Up @@ -81,8 +86,8 @@ export function extractSchema(): Map<string, string[]> | 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,
Expand Down Expand Up @@ -154,6 +159,22 @@ export function extractSchema(): Map<string, string[]> | 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.
// `<circle>` -> `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;
Expand All @@ -166,7 +187,12 @@ function assertNoMissingTags(descMap: Map<string, string[]>): 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(',')}`);
Expand Down