Skip to content

Commit

Permalink
fix(compiler): add math elements to schema (#55631)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
crisbeto committed May 2, 2024
1 parent f978e9c commit 46e0af7
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 2 deletions.
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 @@ -3101,6 +3101,32 @@ export declare class AnimationEvent {
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(Object.entries({
Expand Down
31 changes: 29 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 @@ -72,8 +77,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, visited, descMap, SVG_PREFIX + 'graphics', SVG_PREFIX);
extractProperties(
Expand Down Expand Up @@ -106,6 +111,23 @@ 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 @@ -118,7 +140,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

0 comments on commit 46e0af7

Please sign in to comment.