X Tutup
Skip to content

Commit 5660446

Browse files
committed
feat(HtmlParser): enforce no end tag for void elements
BREAKING CHANGE End tags used to be tolerated for void elements with no content. They are no more allowed so that we more closely follow the HTML5 spec.
1 parent b925ff5 commit 5660446

File tree

6 files changed

+21
-53
lines changed

6 files changed

+21
-53
lines changed

modules/angular2/src/compiler/html_parser.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -171,17 +171,14 @@ class TreeBuilder {
171171
private _consumeEndTag(endTagToken: HtmlToken) {
172172
var fullName =
173173
getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
174-
if (!this._popElement(fullName)) {
175-
let msg;
176174

177-
if (getHtmlTagDefinition(fullName).isVoid) {
178-
msg =
179-
`Void elements do not have end tags (they can not have content) "${endTagToken.parts[1]}"`;
180-
} else {
181-
msg = `Unexpected closing tag "${endTagToken.parts[1]}"`;
182-
}
183-
184-
this.errors.push(HtmlTreeError.create(fullName, endTagToken.sourceSpan.start, msg));
175+
if (getHtmlTagDefinition(fullName).isVoid) {
176+
this.errors.push(
177+
HtmlTreeError.create(fullName, endTagToken.sourceSpan.start,
178+
`Void elements do not have end tags "${endTagToken.parts[1]}"`));
179+
} else if (!this._popElement(fullName)) {
180+
this.errors.push(HtmlTreeError.create(fullName, endTagToken.sourceSpan.start,
181+
`Unexpected closing tag "${endTagToken.parts[1]}"`));
185182
}
186183
}
187184

modules/angular2/test/compiler/html_parser_spec.ts

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,6 @@ export function main() {
8585
]);
8686
});
8787

88-
it('should tolerate end tags for void elements when they have no content', () => {
89-
expect(humanizeDom(parser.parse('<input></input>', 'TestComp')))
90-
.toEqual([[HtmlElementAst, 'input', 0]]);
91-
});
92-
9388
it('should support optional end tags', () => {
9489
expect(humanizeDom(parser.parse('<div><p>1<p>2</div>', 'TestComp')))
9590
.toEqual([
@@ -214,30 +209,11 @@ export function main() {
214209
expect(humanizeErrors(errors)).toEqual([['p', 'Unexpected closing tag "p"', '0:5']]);
215210
});
216211

217-
it('should report text content in void elements', () => {
218-
let errors = parser.parse('<input>content</input>', 'TestComp').errors;
219-
expect(errors.length).toEqual(1);
220-
expect(humanizeErrors(errors))
221-
.toEqual([
222-
[
223-
'input',
224-
'Void elements do not have end tags (they can not have content) "input"',
225-
'0:14'
226-
]
227-
]);
228-
});
229-
230-
it('should report html content in void elements', () => {
231-
let errors = parser.parse('<input><p></p></input>', 'TestComp').errors;
212+
it('should report closing tag for void elements', () => {
213+
let errors = parser.parse('<input></input>', 'TestComp').errors;
232214
expect(errors.length).toEqual(1);
233215
expect(humanizeErrors(errors))
234-
.toEqual([
235-
[
236-
'input',
237-
'Void elements do not have end tags (they can not have content) "input"',
238-
'0:14'
239-
]
240-
]);
216+
.toEqual([['input', 'Void elements do not have end tags "input"', '0:7']]);
241217
});
242218

243219
it('should also report lexer errors', () => {

modules/angular2/test/compiler/template_normalizer_spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ export function main() {
241241
var template = normalizer.normalizeLoadedTemplate(
242242
dirType,
243243
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
244-
'<link href="b" rel="a"></link>', 'package:some/module/');
244+
'<link href="b" rel="a">', 'package:some/module/');
245245
expect(template.styleUrls).toEqual([]);
246246
}));
247247

@@ -250,8 +250,7 @@ export function main() {
250250
var template = normalizer.normalizeLoadedTemplate(
251251
dirType,
252252
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
253-
'<link href="http://some/external.css" rel="stylesheet"></link>',
254-
'package:some/module/');
253+
'<link href="http://some/external.css" rel="stylesheet">', 'package:some/module/');
255254
expect(template.styleUrls).toEqual([]);
256255
}));
257256

modules/angular2/test/compiler/template_parser_spec.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -774,8 +774,7 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
774774

775775
it('should keep <link rel="stylesheet"> elements if they have an absolute non package: url',
776776
() => {
777-
expect(
778-
humanizeTplAst(parse('<link rel="stylesheet" href="http://someurl"></link>a', [])))
777+
expect(humanizeTplAst(parse('<link rel="stylesheet" href="http://someurl">a', [])))
779778
.toEqual([
780779
[ElementAst, 'link'],
781780
[AttrAst, 'rel', 'stylesheet'],
@@ -785,22 +784,21 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
785784
});
786785

787786
it('should keep <link rel="stylesheet"> elements if they have no uri', () => {
788-
expect(humanizeTplAst(parse('<link rel="stylesheet"></link>a', [])))
787+
expect(humanizeTplAst(parse('<link rel="stylesheet">a', [])))
789788
.toEqual([[ElementAst, 'link'], [AttrAst, 'rel', 'stylesheet'], [TextAst, 'a']]);
790-
expect(humanizeTplAst(parse('<link REL="stylesheet"></link>a', [])))
789+
expect(humanizeTplAst(parse('<link REL="stylesheet">a', [])))
791790
.toEqual([[ElementAst, 'link'], [AttrAst, 'REL', 'stylesheet'], [TextAst, 'a']]);
792791
});
793792

794793
it('should ignore <link rel="stylesheet"> elements if they have a relative uri', () => {
795-
expect(humanizeTplAst(parse('<link rel="stylesheet" href="./other.css"></link>a', [])))
794+
expect(humanizeTplAst(parse('<link rel="stylesheet" href="./other.css">a', [])))
796795
.toEqual([[TextAst, 'a']]);
797-
expect(humanizeTplAst(parse('<link rel="stylesheet" HREF="./other.css"></link>a', [])))
796+
expect(humanizeTplAst(parse('<link rel="stylesheet" HREF="./other.css">a', [])))
798797
.toEqual([[TextAst, 'a']]);
799798
});
800799

801800
it('should ignore <link rel="stylesheet"> elements if they have a package: uri', () => {
802-
expect(humanizeTplAst(
803-
parse('<link rel="stylesheet" href="package:somePackage"></link>a', [])))
801+
expect(humanizeTplAst(parse('<link rel="stylesheet" href="package:somePackage">a', [])))
804802
.toEqual([[TextAst, 'a']]);
805803
});
806804

@@ -835,8 +833,7 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
835833

836834
it('should ignore <link rel="stylesheet"> elements inside of elements with ng-non-bindable',
837835
() => {
838-
expect(humanizeTplAst(
839-
parse('<div ng-non-bindable><link rel="stylesheet"></link>a</div>', [])))
836+
expect(humanizeTplAst(parse('<div ng-non-bindable><link rel="stylesheet">a</div>', [])))
840837
.toEqual([[ElementAst, 'div'], [AttrAst, 'ng-non-bindable', ''], [TextAst, 'a']]);
841838
});
842839

modules/angular2/test/core/linker/integration_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ export function main() {
10071007
tcb.overrideView(
10081008
MyComp, new ViewMetadata({
10091009
template:
1010-
'<input type="checkbox" listenerprevent></input><input type="checkbox" listenernoprevent></input>',
1010+
'<input type="checkbox" listenerprevent><input type="checkbox" listenernoprevent>',
10111011
directives: [
10121012
DirectiveListeningDomEventPrevent,
10131013
DirectiveListeningDomEventNoPrevent

modules/angular2/test/web_workers/worker/renderer_integration_spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,7 @@ export function main() {
211211
it('should call actions on the element independent of the compilation',
212212
inject([TestComponentBuilder, Renderer, AsyncTestCompleter],
213213
(tcb: TestComponentBuilder, renderer: Renderer, async) => {
214-
tcb.overrideView(MyComp,
215-
new ViewMetadata({template: '<input [title]="y"></input>'}))
214+
tcb.overrideView(MyComp, new ViewMetadata({template: '<input [title]="y">'}))
216215
.createAsync(MyComp)
217216
.then((fixture) => {
218217
var elRef = fixture.debugElement.componentViewChildren[0].elementRef;

0 commit comments

Comments
 (0)
X Tutup