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