193fb6ee3Sopenharmony_ciimport { 293fb6ee3Sopenharmony_ci Tokenizer, 393fb6ee3Sopenharmony_ci type TokenizerOptions, 493fb6ee3Sopenharmony_ci TokenizerMode, 593fb6ee3Sopenharmony_ci type TokenHandler, 693fb6ee3Sopenharmony_ci Token, 793fb6ee3Sopenharmony_ci foreignContent, 893fb6ee3Sopenharmony_ci html, 993fb6ee3Sopenharmony_ci} from 'parse5'; 1093fb6ee3Sopenharmony_ci 1193fb6ee3Sopenharmony_ciconst $ = html.TAG_ID; 1293fb6ee3Sopenharmony_ci 1393fb6ee3Sopenharmony_ciconst REPLACEMENT_CHARACTER = '\uFFFD'; 1493fb6ee3Sopenharmony_ciconst LINE_FEED_CODE_POINT = 0x0a; 1593fb6ee3Sopenharmony_ci 1693fb6ee3Sopenharmony_ci/** 1793fb6ee3Sopenharmony_ci * Simulates adjustments of the Tokenizer which are performed by the standard parser during tree construction. 1893fb6ee3Sopenharmony_ci */ 1993fb6ee3Sopenharmony_ciexport class ParserFeedbackSimulator implements TokenHandler { 2093fb6ee3Sopenharmony_ci private namespaceStack: html.NS[] = []; 2193fb6ee3Sopenharmony_ci public inForeignContent = false; 2293fb6ee3Sopenharmony_ci public skipNextNewLine = false; 2393fb6ee3Sopenharmony_ci public tokenizer: Tokenizer; 2493fb6ee3Sopenharmony_ci 2593fb6ee3Sopenharmony_ci constructor(options: TokenizerOptions, private handler: TokenHandler) { 2693fb6ee3Sopenharmony_ci this.tokenizer = new Tokenizer(options, this); 2793fb6ee3Sopenharmony_ci this._enterNamespace(html.NS.HTML); 2893fb6ee3Sopenharmony_ci } 2993fb6ee3Sopenharmony_ci 3093fb6ee3Sopenharmony_ci /** @internal */ 3193fb6ee3Sopenharmony_ci onNullCharacter(token: Token.CharacterToken): void { 3293fb6ee3Sopenharmony_ci this.skipNextNewLine = false; 3393fb6ee3Sopenharmony_ci 3493fb6ee3Sopenharmony_ci if (this.inForeignContent) { 3593fb6ee3Sopenharmony_ci this.handler.onCharacter({ 3693fb6ee3Sopenharmony_ci type: Token.TokenType.CHARACTER, 3793fb6ee3Sopenharmony_ci chars: REPLACEMENT_CHARACTER, 3893fb6ee3Sopenharmony_ci location: token.location, 3993fb6ee3Sopenharmony_ci }); 4093fb6ee3Sopenharmony_ci } else { 4193fb6ee3Sopenharmony_ci this.handler.onNullCharacter(token); 4293fb6ee3Sopenharmony_ci } 4393fb6ee3Sopenharmony_ci } 4493fb6ee3Sopenharmony_ci 4593fb6ee3Sopenharmony_ci /** @internal */ 4693fb6ee3Sopenharmony_ci onWhitespaceCharacter(token: Token.CharacterToken): void { 4793fb6ee3Sopenharmony_ci if (this.skipNextNewLine && token.chars.charCodeAt(0) === LINE_FEED_CODE_POINT) { 4893fb6ee3Sopenharmony_ci this.skipNextNewLine = false; 4993fb6ee3Sopenharmony_ci 5093fb6ee3Sopenharmony_ci if (token.chars.length === 1) { 5193fb6ee3Sopenharmony_ci return; 5293fb6ee3Sopenharmony_ci } 5393fb6ee3Sopenharmony_ci 5493fb6ee3Sopenharmony_ci token.chars = token.chars.substr(1); 5593fb6ee3Sopenharmony_ci } 5693fb6ee3Sopenharmony_ci 5793fb6ee3Sopenharmony_ci this.handler.onWhitespaceCharacter(token); 5893fb6ee3Sopenharmony_ci } 5993fb6ee3Sopenharmony_ci 6093fb6ee3Sopenharmony_ci /** @internal */ 6193fb6ee3Sopenharmony_ci onCharacter(token: Token.CharacterToken): void { 6293fb6ee3Sopenharmony_ci this.skipNextNewLine = false; 6393fb6ee3Sopenharmony_ci this.handler.onCharacter(token); 6493fb6ee3Sopenharmony_ci } 6593fb6ee3Sopenharmony_ci 6693fb6ee3Sopenharmony_ci /** @internal */ 6793fb6ee3Sopenharmony_ci onComment(token: Token.CommentToken): void { 6893fb6ee3Sopenharmony_ci this.skipNextNewLine = false; 6993fb6ee3Sopenharmony_ci this.handler.onComment(token); 7093fb6ee3Sopenharmony_ci } 7193fb6ee3Sopenharmony_ci 7293fb6ee3Sopenharmony_ci /** @internal */ 7393fb6ee3Sopenharmony_ci onDoctype(token: Token.DoctypeToken): void { 7493fb6ee3Sopenharmony_ci this.skipNextNewLine = false; 7593fb6ee3Sopenharmony_ci this.handler.onDoctype(token); 7693fb6ee3Sopenharmony_ci } 7793fb6ee3Sopenharmony_ci 7893fb6ee3Sopenharmony_ci /** @internal */ 7993fb6ee3Sopenharmony_ci onEof(token: Token.EOFToken): void { 8093fb6ee3Sopenharmony_ci this.skipNextNewLine = false; 8193fb6ee3Sopenharmony_ci this.handler.onEof(token); 8293fb6ee3Sopenharmony_ci } 8393fb6ee3Sopenharmony_ci 8493fb6ee3Sopenharmony_ci //Namespace stack mutations 8593fb6ee3Sopenharmony_ci private _enterNamespace(namespace: html.NS): void { 8693fb6ee3Sopenharmony_ci this.namespaceStack.unshift(namespace); 8793fb6ee3Sopenharmony_ci this.inForeignContent = namespace !== html.NS.HTML; 8893fb6ee3Sopenharmony_ci this.tokenizer.inForeignNode = this.inForeignContent; 8993fb6ee3Sopenharmony_ci } 9093fb6ee3Sopenharmony_ci 9193fb6ee3Sopenharmony_ci private _leaveCurrentNamespace(): void { 9293fb6ee3Sopenharmony_ci this.namespaceStack.shift(); 9393fb6ee3Sopenharmony_ci this.inForeignContent = this.namespaceStack[0] !== html.NS.HTML; 9493fb6ee3Sopenharmony_ci this.tokenizer.inForeignNode = this.inForeignContent; 9593fb6ee3Sopenharmony_ci } 9693fb6ee3Sopenharmony_ci 9793fb6ee3Sopenharmony_ci //Token handlers 9893fb6ee3Sopenharmony_ci private _ensureTokenizerMode(tn: html.TAG_ID): void { 9993fb6ee3Sopenharmony_ci switch (tn) { 10093fb6ee3Sopenharmony_ci case $.TEXTAREA: 10193fb6ee3Sopenharmony_ci case $.TITLE: { 10293fb6ee3Sopenharmony_ci this.tokenizer.state = TokenizerMode.RCDATA; 10393fb6ee3Sopenharmony_ci break; 10493fb6ee3Sopenharmony_ci } 10593fb6ee3Sopenharmony_ci case $.PLAINTEXT: { 10693fb6ee3Sopenharmony_ci this.tokenizer.state = TokenizerMode.PLAINTEXT; 10793fb6ee3Sopenharmony_ci break; 10893fb6ee3Sopenharmony_ci } 10993fb6ee3Sopenharmony_ci case $.SCRIPT: { 11093fb6ee3Sopenharmony_ci this.tokenizer.state = TokenizerMode.SCRIPT_DATA; 11193fb6ee3Sopenharmony_ci break; 11293fb6ee3Sopenharmony_ci } 11393fb6ee3Sopenharmony_ci case $.STYLE: 11493fb6ee3Sopenharmony_ci case $.IFRAME: 11593fb6ee3Sopenharmony_ci case $.XMP: 11693fb6ee3Sopenharmony_ci case $.NOEMBED: 11793fb6ee3Sopenharmony_ci case $.NOFRAMES: 11893fb6ee3Sopenharmony_ci case $.NOSCRIPT: { 11993fb6ee3Sopenharmony_ci this.tokenizer.state = TokenizerMode.RAWTEXT; 12093fb6ee3Sopenharmony_ci break; 12193fb6ee3Sopenharmony_ci } 12293fb6ee3Sopenharmony_ci default: 12393fb6ee3Sopenharmony_ci // Do nothing 12493fb6ee3Sopenharmony_ci } 12593fb6ee3Sopenharmony_ci } 12693fb6ee3Sopenharmony_ci 12793fb6ee3Sopenharmony_ci /** @internal */ 12893fb6ee3Sopenharmony_ci onStartTag(token: Token.TagToken): void { 12993fb6ee3Sopenharmony_ci let tn = token.tagID; 13093fb6ee3Sopenharmony_ci 13193fb6ee3Sopenharmony_ci switch (tn) { 13293fb6ee3Sopenharmony_ci case $.SVG: { 13393fb6ee3Sopenharmony_ci this._enterNamespace(html.NS.SVG); 13493fb6ee3Sopenharmony_ci break; 13593fb6ee3Sopenharmony_ci } 13693fb6ee3Sopenharmony_ci case $.MATH: { 13793fb6ee3Sopenharmony_ci this._enterNamespace(html.NS.MATHML); 13893fb6ee3Sopenharmony_ci break; 13993fb6ee3Sopenharmony_ci } 14093fb6ee3Sopenharmony_ci default: 14193fb6ee3Sopenharmony_ci // Do nothing 14293fb6ee3Sopenharmony_ci } 14393fb6ee3Sopenharmony_ci 14493fb6ee3Sopenharmony_ci if (this.inForeignContent) { 14593fb6ee3Sopenharmony_ci if (foreignContent.causesExit(token)) { 14693fb6ee3Sopenharmony_ci this._leaveCurrentNamespace(); 14793fb6ee3Sopenharmony_ci } else { 14893fb6ee3Sopenharmony_ci const currentNs = this.namespaceStack[0]; 14993fb6ee3Sopenharmony_ci tn = token.tagID; 15093fb6ee3Sopenharmony_ci 15193fb6ee3Sopenharmony_ci if (!token.selfClosing && foreignContent.isIntegrationPoint(tn, currentNs, token.attrs)) { 15293fb6ee3Sopenharmony_ci this._enterNamespace(html.NS.HTML); 15393fb6ee3Sopenharmony_ci } 15493fb6ee3Sopenharmony_ci } 15593fb6ee3Sopenharmony_ci } else { 15693fb6ee3Sopenharmony_ci switch (tn) { 15793fb6ee3Sopenharmony_ci case $.PRE: 15893fb6ee3Sopenharmony_ci case $.TEXTAREA: 15993fb6ee3Sopenharmony_ci case $.LISTING: { 16093fb6ee3Sopenharmony_ci this.skipNextNewLine = true; 16193fb6ee3Sopenharmony_ci break; 16293fb6ee3Sopenharmony_ci } 16393fb6ee3Sopenharmony_ci case $.IMAGE: { 16493fb6ee3Sopenharmony_ci token.tagName = html.TAG_NAMES.IMG; 16593fb6ee3Sopenharmony_ci token.tagID = $.IMG; 16693fb6ee3Sopenharmony_ci break; 16793fb6ee3Sopenharmony_ci } 16893fb6ee3Sopenharmony_ci default: 16993fb6ee3Sopenharmony_ci // Do nothing 17093fb6ee3Sopenharmony_ci } 17193fb6ee3Sopenharmony_ci 17293fb6ee3Sopenharmony_ci this._ensureTokenizerMode(tn); 17393fb6ee3Sopenharmony_ci } 17493fb6ee3Sopenharmony_ci 17593fb6ee3Sopenharmony_ci this.handler.onStartTag(token); 17693fb6ee3Sopenharmony_ci } 17793fb6ee3Sopenharmony_ci 17893fb6ee3Sopenharmony_ci /** @internal */ 17993fb6ee3Sopenharmony_ci onEndTag(token: Token.TagToken): void { 18093fb6ee3Sopenharmony_ci let tn = token.tagID; 18193fb6ee3Sopenharmony_ci 18293fb6ee3Sopenharmony_ci if (!this.inForeignContent) { 18393fb6ee3Sopenharmony_ci const previousNs = this.namespaceStack[1]; 18493fb6ee3Sopenharmony_ci 18593fb6ee3Sopenharmony_ci if (previousNs === html.NS.SVG) { 18693fb6ee3Sopenharmony_ci const adjustedTagName = foreignContent.SVG_TAG_NAMES_ADJUSTMENT_MAP.get(token.tagName); 18793fb6ee3Sopenharmony_ci 18893fb6ee3Sopenharmony_ci if (adjustedTagName) { 18993fb6ee3Sopenharmony_ci tn = html.getTagID(adjustedTagName); 19093fb6ee3Sopenharmony_ci } 19193fb6ee3Sopenharmony_ci } 19293fb6ee3Sopenharmony_ci 19393fb6ee3Sopenharmony_ci //NOTE: check for exit from integration point 19493fb6ee3Sopenharmony_ci if (foreignContent.isIntegrationPoint(tn, previousNs, token.attrs)) { 19593fb6ee3Sopenharmony_ci this._leaveCurrentNamespace(); 19693fb6ee3Sopenharmony_ci } 19793fb6ee3Sopenharmony_ci } else if ( 19893fb6ee3Sopenharmony_ci (tn === $.SVG && this.namespaceStack[0] === html.NS.SVG) || 19993fb6ee3Sopenharmony_ci (tn === $.MATH && this.namespaceStack[0] === html.NS.MATHML) 20093fb6ee3Sopenharmony_ci ) { 20193fb6ee3Sopenharmony_ci this._leaveCurrentNamespace(); 20293fb6ee3Sopenharmony_ci } 20393fb6ee3Sopenharmony_ci 20493fb6ee3Sopenharmony_ci this.handler.onEndTag(token); 20593fb6ee3Sopenharmony_ci } 20693fb6ee3Sopenharmony_ci} 207