fix(HtmlParser): do not add a tbody parent for tr inside thead & tfoot
fixes #5403
This commit is contained in:
		
							parent
							
								
									bdfed9d850
								
							
						
					
					
						commit
						c58e7e0e91
					
				| @ -145,7 +145,7 @@ class TreeBuilder { | |||||||
|     var tagDef = getHtmlTagDefinition(el.name); |     var tagDef = getHtmlTagDefinition(el.name); | ||||||
|     var parentEl = this._getParentElement(); |     var parentEl = this._getParentElement(); | ||||||
|     if (tagDef.requireExtraParent(isPresent(parentEl) ? parentEl.name : null)) { |     if (tagDef.requireExtraParent(isPresent(parentEl) ? parentEl.name : null)) { | ||||||
|       var newParent = new HtmlElementAst(tagDef.requiredParent, [], [el], el.sourceSpan); |       var newParent = new HtmlElementAst(tagDef.parentToAdd, [], [el], el.sourceSpan); | ||||||
|       this._addToParent(newParent); |       this._addToParent(newParent); | ||||||
|       this.elementStack.push(newParent); |       this.elementStack.push(newParent); | ||||||
|       this.elementStack.push(el); |       this.elementStack.push(el); | ||||||
|  | |||||||
| @ -66,30 +66,35 @@ export enum HtmlTagContentType { | |||||||
| export class HtmlTagDefinition { | export class HtmlTagDefinition { | ||||||
|   private closedByChildren: {[key: string]: boolean} = {}; |   private closedByChildren: {[key: string]: boolean} = {}; | ||||||
|   public closedByParent: boolean = false; |   public closedByParent: boolean = false; | ||||||
|   public requiredParent: string; |   public requiredParents: {[key: string]: boolean}; | ||||||
|  |   public parentToAdd: string; | ||||||
|   public implicitNamespacePrefix: string; |   public implicitNamespacePrefix: string; | ||||||
|   public contentType: HtmlTagContentType; |   public contentType: HtmlTagContentType; | ||||||
| 
 | 
 | ||||||
|   constructor({closedByChildren, requiredParent, implicitNamespacePrefix, contentType, |   constructor({closedByChildren, requiredParents, implicitNamespacePrefix, contentType, | ||||||
|                closedByParent}: { |                closedByParent}: { | ||||||
|     closedByChildren?: string, |     closedByChildren?: string[], | ||||||
|     closedByParent?: boolean, |     closedByParent?: boolean, | ||||||
|     requiredParent?: string, |     requiredParents?: string[], | ||||||
|     implicitNamespacePrefix?: string, |     implicitNamespacePrefix?: string, | ||||||
|     contentType?: HtmlTagContentType |     contentType?: HtmlTagContentType | ||||||
|   } = {}) { |   } = {}) { | ||||||
|     if (isPresent(closedByChildren) && closedByChildren.length > 0) { |     if (isPresent(closedByChildren) && closedByChildren.length > 0) { | ||||||
|       closedByChildren.split(',').forEach(tagName => this.closedByChildren[tagName.trim()] = true); |       closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true); | ||||||
|     } |     } | ||||||
|     this.closedByParent = normalizeBool(closedByParent); |     this.closedByParent = normalizeBool(closedByParent); | ||||||
|     this.requiredParent = requiredParent; |     if (isPresent(requiredParents) && requiredParents.length > 0) { | ||||||
|  |       this.requiredParents = {}; | ||||||
|  |       this.parentToAdd = requiredParents[0]; | ||||||
|  |       requiredParents.forEach(tagName => this.requiredParents[tagName] = true); | ||||||
|  |     } | ||||||
|     this.implicitNamespacePrefix = implicitNamespacePrefix; |     this.implicitNamespacePrefix = implicitNamespacePrefix; | ||||||
|     this.contentType = isPresent(contentType) ? contentType : HtmlTagContentType.PARSABLE_DATA; |     this.contentType = isPresent(contentType) ? contentType : HtmlTagContentType.PARSABLE_DATA; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   requireExtraParent(currentParent: string): boolean { |   requireExtraParent(currentParent: string): boolean { | ||||||
|     return isPresent(this.requiredParent) && |     return isPresent(this.requiredParents) && | ||||||
|            (isBlank(currentParent) || this.requiredParent != currentParent.toLowerCase()); |            (isBlank(currentParent) || this.requiredParents[currentParent.toLowerCase()] != true); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   isClosedByChild(name: string): boolean { |   isClosedByChild(name: string): boolean { | ||||||
| @ -101,37 +106,66 @@ export class HtmlTagDefinition { | |||||||
| // see http://www.w3.org/TR/html51/syntax.html#optional-tags
 | // see http://www.w3.org/TR/html51/syntax.html#optional-tags
 | ||||||
| // This implementation does not fully conform to the HTML5 spec.
 | // This implementation does not fully conform to the HTML5 spec.
 | ||||||
| var TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = { | var TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = { | ||||||
|   'link': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}), |   'link': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}), | ||||||
|   'ng-content': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}), |   'ng-content': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}), | ||||||
|   'img': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}), |   'img': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}), | ||||||
|   'input': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}), |   'input': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}), | ||||||
|   'hr': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}), |   'hr': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}), | ||||||
|   'br': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}), |   'br': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}), | ||||||
|   'wbr': new HtmlTagDefinition({closedByChildren: '*', closedByParent: true}), |   'wbr': new HtmlTagDefinition({closedByChildren: ['*'], closedByParent: true}), | ||||||
|   'p': new HtmlTagDefinition({ |   'p': new HtmlTagDefinition({ | ||||||
|     closedByChildren: |     closedByChildren: [ | ||||||
|         'address,article,aside,blockquote,div,dl,fieldset,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,main,nav,ol,p,pre,section,table,ul', |       'address', | ||||||
|  |       'article', | ||||||
|  |       'aside', | ||||||
|  |       'blockquote', | ||||||
|  |       'div', | ||||||
|  |       'dl', | ||||||
|  |       'fieldset', | ||||||
|  |       'footer', | ||||||
|  |       'form', | ||||||
|  |       'h1', | ||||||
|  |       'h2', | ||||||
|  |       'h3', | ||||||
|  |       'h4', | ||||||
|  |       'h5', | ||||||
|  |       'h6', | ||||||
|  |       'header', | ||||||
|  |       'hgroup', | ||||||
|  |       'hr', | ||||||
|  |       'main', | ||||||
|  |       'nav', | ||||||
|  |       'ol', | ||||||
|  |       'p', | ||||||
|  |       'pre', | ||||||
|  |       'section', | ||||||
|  |       'table', | ||||||
|  |       'ul' | ||||||
|  |     ], | ||||||
|     closedByParent: true |     closedByParent: true | ||||||
|   }), |   }), | ||||||
|   'thead': new HtmlTagDefinition({closedByChildren: 'tbody,tfoot'}), |   'thead': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot']}), | ||||||
|   'tbody': new HtmlTagDefinition({closedByChildren: 'tbody,tfoot', closedByParent: true}), |   'tbody': new HtmlTagDefinition({closedByChildren: ['tbody', 'tfoot'], closedByParent: true}), | ||||||
|   'tfoot': new HtmlTagDefinition({closedByChildren: 'tbody', closedByParent: true}), |   'tfoot': new HtmlTagDefinition({closedByChildren: ['tbody'], closedByParent: true}), | ||||||
|   'tr': new HtmlTagDefinition( |   'tr': new HtmlTagDefinition({ | ||||||
|       {closedByChildren: 'tr', requiredParent: 'tbody', closedByParent: true}), |     closedByChildren: ['tr'], | ||||||
|   'td': new HtmlTagDefinition({closedByChildren: 'td,th', closedByParent: true}), |     requiredParents: ['tbody', 'tfoot', 'thead'], | ||||||
|   'th': new HtmlTagDefinition({closedByChildren: 'td,th', closedByParent: true}), |     closedByParent: true | ||||||
|   'col': new HtmlTagDefinition({closedByChildren: 'col', requiredParent: 'colgroup'}), |   }), | ||||||
|  |   'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}), | ||||||
|  |   'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}), | ||||||
|  |   'col': new HtmlTagDefinition({closedByChildren: ['col'], requiredParents: ['colgroup']}), | ||||||
|   'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}), |   'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}), | ||||||
|   'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}), |   'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}), | ||||||
|   'li': new HtmlTagDefinition({closedByChildren: 'li', closedByParent: true}), |   'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}), | ||||||
|   'dt': new HtmlTagDefinition({closedByChildren: 'dt,dd'}), |   'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}), | ||||||
|   'dd': new HtmlTagDefinition({closedByChildren: 'dt,dd', closedByParent: true}), |   'dd': new HtmlTagDefinition({closedByChildren: ['dt', 'dd'], closedByParent: true}), | ||||||
|   'rb': new HtmlTagDefinition({closedByChildren: 'rb,rt,rtc,rp', closedByParent: true}), |   'rb': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}), | ||||||
|   'rt': new HtmlTagDefinition({closedByChildren: 'rb,rt,rtc,rp', closedByParent: true}), |   'rt': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}), | ||||||
|   'rtc': new HtmlTagDefinition({closedByChildren: 'rb,rtc,rp', closedByParent: true}), |   'rtc': new HtmlTagDefinition({closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true}), | ||||||
|   'rp': new HtmlTagDefinition({closedByChildren: 'rb,rt,rtc,rp', closedByParent: true}), |   'rp': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true}), | ||||||
|   'optgroup': new HtmlTagDefinition({closedByChildren: 'optgroup', closedByParent: true}), |   'optgroup': new HtmlTagDefinition({closedByChildren: ['optgroup'], closedByParent: true}), | ||||||
|   'option': new HtmlTagDefinition({closedByChildren: 'option,optgroup', closedByParent: true}), |   'option': new HtmlTagDefinition({closedByChildren: ['option', 'optgroup'], closedByParent: true}), | ||||||
|   'style': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}), |   'style': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}), | ||||||
|   'script': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}), |   'script': new HtmlTagDefinition({contentType: HtmlTagContentType.RAW_TEXT}), | ||||||
|   'title': new HtmlTagDefinition({contentType: HtmlTagContentType.ESCAPABLE_RAW_TEXT}), |   'title': new HtmlTagDefinition({contentType: HtmlTagContentType.ESCAPABLE_RAW_TEXT}), | ||||||
|  | |||||||
| @ -97,11 +97,24 @@ export function main() { | |||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         it('should add the requiredParent', () => { |         it('should add the requiredParent', () => { | ||||||
|           expect(humanizeDom(parser.parse('<table><tr></tr></table>', 'TestComp'))) |           expect( | ||||||
|  |               humanizeDom(parser.parse( | ||||||
|  |                   '<table><thead><tr head></tr></thead><tr noparent></tr><tbody><tr body></tr></tbody><tfoot><tr foot></tr></tfoot></table>', | ||||||
|  |                   'TestComp'))) | ||||||
|               .toEqual([ |               .toEqual([ | ||||||
|                 [HtmlElementAst, 'table', 0], |                 [HtmlElementAst, 'table', 0], | ||||||
|  |                 [HtmlElementAst, 'thead', 1], | ||||||
|  |                 [HtmlElementAst, 'tr', 2], | ||||||
|  |                 [HtmlAttrAst, 'head', ''], | ||||||
|                 [HtmlElementAst, 'tbody', 1], |                 [HtmlElementAst, 'tbody', 1], | ||||||
|                 [HtmlElementAst, 'tr', 2], |                 [HtmlElementAst, 'tr', 2], | ||||||
|  |                 [HtmlAttrAst, 'noparent', ''], | ||||||
|  |                 [HtmlElementAst, 'tbody', 1], | ||||||
|  |                 [HtmlElementAst, 'tr', 2], | ||||||
|  |                 [HtmlAttrAst, 'body', ''], | ||||||
|  |                 [HtmlElementAst, 'tfoot', 1], | ||||||
|  |                 [HtmlElementAst, 'tr', 2], | ||||||
|  |                 [HtmlAttrAst, 'foot', ''] | ||||||
|               ]); |               ]); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user