Skip to content

Commit

Permalink
feat(HtmlParser): enforce only void & foreign elts can be self closed
Browse files Browse the repository at this point in the history
BREAKING CHANGE:

`<whatever />` used to be expanded to `<whatever></whatever>`.
The parser now follows the HTML5 spec more closely.
Only void and foreign elements can be self closed.

Closes #5591
  • Loading branch information
vicb committed Dec 4, 2015
1 parent 5660446 commit d388c0a
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 10 deletions.
5 changes: 5 additions & 0 deletions modules/angular2/src/compiler/html_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ class TreeBuilder {
if (this.peek.type === HtmlTokenType.TAG_OPEN_END_VOID) {
this._advance();
selfClosing = true;
if (namespacePrefix(fullName) == null && !getHtmlTagDefinition(fullName).isVoid) {
this.errors.push(HtmlTreeError.create(
fullName, startTagToken.sourceSpan.start,
`Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
}
} else if (this.peek.type === HtmlTokenType.TAG_OPEN_END) {
this._advance();
selfClosing = false;
Expand Down
30 changes: 28 additions & 2 deletions modules/angular2/test/compiler/html_parser_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ export function main() {
expect(humanizeDom(parser.parse('<DiV><P></p></dIv>', 'TestComp')))
.toEqual([[HtmlElementAst, 'DiV', 0], [HtmlElementAst, 'P', 1]]);
});

it('should support self closing void elements', () => {
expect(humanizeDom(parser.parse('<input />', 'TestComp')))
.toEqual([[HtmlElementAst, 'input', 0]]);
});

it('should support self closing foreign elements', () => {
expect(humanizeDom(parser.parse('<math />', 'TestComp')))
.toEqual([[HtmlElementAst, '@math:math', 0]]);
});
});

describe('attributes', () => {
Expand Down Expand Up @@ -175,8 +185,8 @@ export function main() {
});

it('should support mamespace', () => {
expect(humanizeDom(parser.parse('<use xlink:href="Port" />', 'TestComp')))
.toEqual([[HtmlElementAst, 'use', 0], [HtmlAttrAst, '@xlink:href', 'Port']]);
expect(humanizeDom(parser.parse('<svg:use xlink:href="Port" />', 'TestComp')))
.toEqual([[HtmlElementAst, '@svg:use', 0], [HtmlAttrAst, '@xlink:href', 'Port']]);
});
});

Expand Down Expand Up @@ -216,6 +226,22 @@ export function main() {
.toEqual([['input', 'Void elements do not have end tags "input"', '0:7']]);
});

it('should report self closing html element', () => {
let errors = parser.parse('<p />', 'TestComp').errors;
expect(errors.length).toEqual(1);
expect(humanizeErrors(errors))
.toEqual([['p', 'Only void and foreign elements can be self closed "p"', '0:0']]);
});

it('should report self closing custom element', () => {
let errors = parser.parse('<my-cmp />', 'TestComp').errors;
expect(errors.length).toEqual(1);
expect(humanizeErrors(errors))
.toEqual([
['my-cmp', 'Only void and foreign elements can be self closed "my-cmp"', '0:0']
]);
});

it('should also report lexer errors', () => {
let errors = parser.parse('<!-err--><div></p></div>', 'TestComp').errors;
expect(errors.length).toEqual(2);
Expand Down
4 changes: 2 additions & 2 deletions modules/angular2/test/compiler/template_parser_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,8 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
type: new CompileTypeMetadata({name: 'DirB'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(() => parse('<div/>', [dirB, dirA])).toThrowError(`Template parse errors:
More than one component: DirB,DirA ("[ERROR ->]<div/>"): TestComp@0:0`);
expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
More than one component: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`);
});

it('should not allow components or element bindings nor dom events on explicit embedded templates',
Expand Down
10 changes: 6 additions & 4 deletions modules/angular2/test/core/linker/integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,10 +578,12 @@ export function main() {
inject(
[TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata({
template: '<p><child-cmp var-alice/><child-cmp var-bob/></p>',
directives: [ChildComp]
}))
tcb.overrideView(
MyComp, new ViewMetadata({
template:
'<p><child-cmp var-alice></child-cmp><child-cmp var-bob></child-cmp></p>',
directives: [ChildComp]
}))

.createAsync(MyComp)
.then((fixture) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ void allTests() {
});

it('should include platform directives.', () async {
fooComponentMeta.template = new CompileTemplateMetadata(template: '<bar/>');
fooComponentMeta.template = new CompileTemplateMetadata(
template: '<bar></bar>');
final viewAnnotation = new AnnotationModel()
..name = 'View'
..isView = true;
Expand All @@ -370,7 +371,8 @@ void allTests() {
});

it('should include platform directives when it it a list.', () async {
fooComponentMeta.template = new CompileTemplateMetadata(template: '<bar/>');
fooComponentMeta.template = new CompileTemplateMetadata(
template: '<bar></bar>');
final viewAnnotation = new AnnotationModel()
..name = 'View'
..isView = true;
Expand Down

0 comments on commit d388c0a

Please sign in to comment.