UNPKG

dynatrace-cordova-outsystems-plugin

Version:

This plugin gives you the ability to use the Dynatrace instrumentation in your hybrid application (Cordova, Ionic, ..). It uses the Mobile Agent, the JavaScript Agent and the Javascript Bridge. The Mobile Agent will give you all device specific values con

1,476 lines (1,238 loc) 93.3 kB
'use strict'; const Tokenizer = require('../tokenizer'); const OpenElementStack = require('./open-element-stack'); const FormattingElementList = require('./formatting-element-list'); const LocationInfoParserMixin = require('../extensions/location-info/parser-mixin'); const ErrorReportingParserMixin = require('../extensions/error-reporting/parser-mixin'); const Mixin = require('../utils/mixin'); const defaultTreeAdapter = require('../tree-adapters/default'); const mergeOptions = require('../utils/merge-options'); const doctype = require('../common/doctype'); const foreignContent = require('../common/foreign-content'); const ERR = require('../common/error-codes'); const unicode = require('../common/unicode'); const HTML = require('../common/html'); //Aliases const $ = HTML.TAG_NAMES; const NS = HTML.NAMESPACES; const ATTRS = HTML.ATTRS; const DEFAULT_OPTIONS = { scriptingEnabled: true, sourceCodeLocationInfo: false, onParseError: null, treeAdapter: defaultTreeAdapter }; //Misc constants const HIDDEN_INPUT_TYPE = 'hidden'; //Adoption agency loops iteration count const AA_OUTER_LOOP_ITER = 8; const AA_INNER_LOOP_ITER = 3; //Insertion modes const INITIAL_MODE = 'INITIAL_MODE'; const BEFORE_HTML_MODE = 'BEFORE_HTML_MODE'; const BEFORE_HEAD_MODE = 'BEFORE_HEAD_MODE'; const IN_HEAD_MODE = 'IN_HEAD_MODE'; const IN_HEAD_NO_SCRIPT_MODE = 'IN_HEAD_NO_SCRIPT_MODE'; const AFTER_HEAD_MODE = 'AFTER_HEAD_MODE'; const IN_BODY_MODE = 'IN_BODY_MODE'; const TEXT_MODE = 'TEXT_MODE'; const IN_TABLE_MODE = 'IN_TABLE_MODE'; const IN_TABLE_TEXT_MODE = 'IN_TABLE_TEXT_MODE'; const IN_CAPTION_MODE = 'IN_CAPTION_MODE'; const IN_COLUMN_GROUP_MODE = 'IN_COLUMN_GROUP_MODE'; const IN_TABLE_BODY_MODE = 'IN_TABLE_BODY_MODE'; const IN_ROW_MODE = 'IN_ROW_MODE'; const IN_CELL_MODE = 'IN_CELL_MODE'; const IN_SELECT_MODE = 'IN_SELECT_MODE'; const IN_SELECT_IN_TABLE_MODE = 'IN_SELECT_IN_TABLE_MODE'; const IN_TEMPLATE_MODE = 'IN_TEMPLATE_MODE'; const AFTER_BODY_MODE = 'AFTER_BODY_MODE'; const IN_FRAMESET_MODE = 'IN_FRAMESET_MODE'; const AFTER_FRAMESET_MODE = 'AFTER_FRAMESET_MODE'; const AFTER_AFTER_BODY_MODE = 'AFTER_AFTER_BODY_MODE'; const AFTER_AFTER_FRAMESET_MODE = 'AFTER_AFTER_FRAMESET_MODE'; //Insertion mode reset map const INSERTION_MODE_RESET_MAP = { [$.TR]: IN_ROW_MODE, [$.TBODY]: IN_TABLE_BODY_MODE, [$.THEAD]: IN_TABLE_BODY_MODE, [$.TFOOT]: IN_TABLE_BODY_MODE, [$.CAPTION]: IN_CAPTION_MODE, [$.COLGROUP]: IN_COLUMN_GROUP_MODE, [$.TABLE]: IN_TABLE_MODE, [$.BODY]: IN_BODY_MODE, [$.FRAMESET]: IN_FRAMESET_MODE }; //Template insertion mode switch map const TEMPLATE_INSERTION_MODE_SWITCH_MAP = { [$.CAPTION]: IN_TABLE_MODE, [$.COLGROUP]: IN_TABLE_MODE, [$.TBODY]: IN_TABLE_MODE, [$.TFOOT]: IN_TABLE_MODE, [$.THEAD]: IN_TABLE_MODE, [$.COL]: IN_COLUMN_GROUP_MODE, [$.TR]: IN_TABLE_BODY_MODE, [$.TD]: IN_ROW_MODE, [$.TH]: IN_ROW_MODE }; //Token handlers map for insertion modes const TOKEN_HANDLERS = { [INITIAL_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenInInitialMode, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInInitialMode, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: doctypeInInitialMode, [Tokenizer.START_TAG_TOKEN]: tokenInInitialMode, [Tokenizer.END_TAG_TOKEN]: tokenInInitialMode, [Tokenizer.EOF_TOKEN]: tokenInInitialMode }, [BEFORE_HTML_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenBeforeHtml, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenBeforeHtml, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagBeforeHtml, [Tokenizer.END_TAG_TOKEN]: endTagBeforeHtml, [Tokenizer.EOF_TOKEN]: tokenBeforeHtml }, [BEFORE_HEAD_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenBeforeHead, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenBeforeHead, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype, [Tokenizer.START_TAG_TOKEN]: startTagBeforeHead, [Tokenizer.END_TAG_TOKEN]: endTagBeforeHead, [Tokenizer.EOF_TOKEN]: tokenBeforeHead }, [IN_HEAD_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenInHead, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInHead, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype, [Tokenizer.START_TAG_TOKEN]: startTagInHead, [Tokenizer.END_TAG_TOKEN]: endTagInHead, [Tokenizer.EOF_TOKEN]: tokenInHead }, [IN_HEAD_NO_SCRIPT_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenInHeadNoScript, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInHeadNoScript, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype, [Tokenizer.START_TAG_TOKEN]: startTagInHeadNoScript, [Tokenizer.END_TAG_TOKEN]: endTagInHeadNoScript, [Tokenizer.EOF_TOKEN]: tokenInHeadNoScript }, [AFTER_HEAD_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenAfterHead, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterHead, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype, [Tokenizer.START_TAG_TOKEN]: startTagAfterHead, [Tokenizer.END_TAG_TOKEN]: endTagAfterHead, [Tokenizer.EOF_TOKEN]: tokenAfterHead }, [IN_BODY_MODE]: { [Tokenizer.CHARACTER_TOKEN]: characterInBody, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInBody, [Tokenizer.END_TAG_TOKEN]: endTagInBody, [Tokenizer.EOF_TOKEN]: eofInBody }, [TEXT_MODE]: { [Tokenizer.CHARACTER_TOKEN]: insertCharacters, [Tokenizer.NULL_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: ignoreToken, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: ignoreToken, [Tokenizer.END_TAG_TOKEN]: endTagInText, [Tokenizer.EOF_TOKEN]: eofInText }, [IN_TABLE_MODE]: { [Tokenizer.CHARACTER_TOKEN]: characterInTable, [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInTable, [Tokenizer.END_TAG_TOKEN]: endTagInTable, [Tokenizer.EOF_TOKEN]: eofInBody }, [IN_TABLE_TEXT_MODE]: { [Tokenizer.CHARACTER_TOKEN]: characterInTableText, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInTableText, [Tokenizer.COMMENT_TOKEN]: tokenInTableText, [Tokenizer.DOCTYPE_TOKEN]: tokenInTableText, [Tokenizer.START_TAG_TOKEN]: tokenInTableText, [Tokenizer.END_TAG_TOKEN]: tokenInTableText, [Tokenizer.EOF_TOKEN]: tokenInTableText }, [IN_CAPTION_MODE]: { [Tokenizer.CHARACTER_TOKEN]: characterInBody, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInCaption, [Tokenizer.END_TAG_TOKEN]: endTagInCaption, [Tokenizer.EOF_TOKEN]: eofInBody }, [IN_COLUMN_GROUP_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenInColumnGroup, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInColumnGroup, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInColumnGroup, [Tokenizer.END_TAG_TOKEN]: endTagInColumnGroup, [Tokenizer.EOF_TOKEN]: eofInBody }, [IN_TABLE_BODY_MODE]: { [Tokenizer.CHARACTER_TOKEN]: characterInTable, [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInTableBody, [Tokenizer.END_TAG_TOKEN]: endTagInTableBody, [Tokenizer.EOF_TOKEN]: eofInBody }, [IN_ROW_MODE]: { [Tokenizer.CHARACTER_TOKEN]: characterInTable, [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInRow, [Tokenizer.END_TAG_TOKEN]: endTagInRow, [Tokenizer.EOF_TOKEN]: eofInBody }, [IN_CELL_MODE]: { [Tokenizer.CHARACTER_TOKEN]: characterInBody, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInCell, [Tokenizer.END_TAG_TOKEN]: endTagInCell, [Tokenizer.EOF_TOKEN]: eofInBody }, [IN_SELECT_MODE]: { [Tokenizer.CHARACTER_TOKEN]: insertCharacters, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInSelect, [Tokenizer.END_TAG_TOKEN]: endTagInSelect, [Tokenizer.EOF_TOKEN]: eofInBody }, [IN_SELECT_IN_TABLE_MODE]: { [Tokenizer.CHARACTER_TOKEN]: insertCharacters, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInSelectInTable, [Tokenizer.END_TAG_TOKEN]: endTagInSelectInTable, [Tokenizer.EOF_TOKEN]: eofInBody }, [IN_TEMPLATE_MODE]: { [Tokenizer.CHARACTER_TOKEN]: characterInBody, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInTemplate, [Tokenizer.END_TAG_TOKEN]: endTagInTemplate, [Tokenizer.EOF_TOKEN]: eofInTemplate }, [AFTER_BODY_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenAfterBody, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterBody, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody, [Tokenizer.COMMENT_TOKEN]: appendCommentToRootHtmlElement, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagAfterBody, [Tokenizer.END_TAG_TOKEN]: endTagAfterBody, [Tokenizer.EOF_TOKEN]: stopParsing }, [IN_FRAMESET_MODE]: { [Tokenizer.CHARACTER_TOKEN]: ignoreToken, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagInFrameset, [Tokenizer.END_TAG_TOKEN]: endTagInFrameset, [Tokenizer.EOF_TOKEN]: stopParsing }, [AFTER_FRAMESET_MODE]: { [Tokenizer.CHARACTER_TOKEN]: ignoreToken, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters, [Tokenizer.COMMENT_TOKEN]: appendComment, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagAfterFrameset, [Tokenizer.END_TAG_TOKEN]: endTagAfterFrameset, [Tokenizer.EOF_TOKEN]: stopParsing }, [AFTER_AFTER_BODY_MODE]: { [Tokenizer.CHARACTER_TOKEN]: tokenAfterAfterBody, [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterAfterBody, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody, [Tokenizer.COMMENT_TOKEN]: appendCommentToDocument, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagAfterAfterBody, [Tokenizer.END_TAG_TOKEN]: tokenAfterAfterBody, [Tokenizer.EOF_TOKEN]: stopParsing }, [AFTER_AFTER_FRAMESET_MODE]: { [Tokenizer.CHARACTER_TOKEN]: ignoreToken, [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken, [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody, [Tokenizer.COMMENT_TOKEN]: appendCommentToDocument, [Tokenizer.DOCTYPE_TOKEN]: ignoreToken, [Tokenizer.START_TAG_TOKEN]: startTagAfterAfterFrameset, [Tokenizer.END_TAG_TOKEN]: ignoreToken, [Tokenizer.EOF_TOKEN]: stopParsing } }; //Parser class Parser { constructor(options) { this.options = mergeOptions(DEFAULT_OPTIONS, options); this.treeAdapter = this.options.treeAdapter; this.pendingScript = null; if (this.options.sourceCodeLocationInfo) { Mixin.install(this, LocationInfoParserMixin); } if (this.options.onParseError) { Mixin.install(this, ErrorReportingParserMixin, { onParseError: this.options.onParseError }); } } // API parse(html) { const document = this.treeAdapter.createDocument(); this._bootstrap(document, null); this.tokenizer.write(html, true); this._runParsingLoop(null); return document; } parseFragment(html, fragmentContext) { //NOTE: use <template> element as a fragment context if context element was not provided, //so we will parse in "forgiving" manner if (!fragmentContext) { fragmentContext = this.treeAdapter.createElement($.TEMPLATE, NS.HTML, []); } //NOTE: create fake element which will be used as 'document' for fragment parsing. //This is important for jsdom there 'document' can't be recreated, therefore //fragment parsing causes messing of the main `document`. const documentMock = this.treeAdapter.createElement('documentmock', NS.HTML, []); this._bootstrap(documentMock, fragmentContext); if (this.treeAdapter.getTagName(fragmentContext) === $.TEMPLATE) { this._pushTmplInsertionMode(IN_TEMPLATE_MODE); } this._initTokenizerForFragmentParsing(); this._insertFakeRootElement(); this._resetInsertionMode(); this._findFormInFragmentContext(); this.tokenizer.write(html, true); this._runParsingLoop(null); const rootElement = this.treeAdapter.getFirstChild(documentMock); const fragment = this.treeAdapter.createDocumentFragment(); this._adoptNodes(rootElement, fragment); return fragment; } //Bootstrap parser _bootstrap(document, fragmentContext) { this.tokenizer = new Tokenizer(this.options); this.stopped = false; this.insertionMode = INITIAL_MODE; this.originalInsertionMode = ''; this.document = document; this.fragmentContext = fragmentContext; this.headElement = null; this.formElement = null; this.openElements = new OpenElementStack(this.document, this.treeAdapter); this.activeFormattingElements = new FormattingElementList(this.treeAdapter); this.tmplInsertionModeStack = []; this.tmplInsertionModeStackTop = -1; this.currentTmplInsertionMode = null; this.pendingCharacterTokens = []; this.hasNonWhitespacePendingCharacterToken = false; this.framesetOk = true; this.skipNextNewLine = false; this.fosterParentingEnabled = false; } //Errors _err() { // NOTE: err reporting is noop by default. Enabled by mixin. } //Parsing loop _runParsingLoop(scriptHandler) { while (!this.stopped) { this._setupTokenizerCDATAMode(); const token = this.tokenizer.getNextToken(); if (token.type === Tokenizer.HIBERNATION_TOKEN) { break; } if (this.skipNextNewLine) { this.skipNextNewLine = false; if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN && token.chars[0] === '\n') { if (token.chars.length === 1) { continue; } token.chars = token.chars.substr(1); } } this._processInputToken(token); if (scriptHandler && this.pendingScript) { break; } } } runParsingLoopForCurrentChunk(writeCallback, scriptHandler) { this._runParsingLoop(scriptHandler); if (scriptHandler && this.pendingScript) { const script = this.pendingScript; this.pendingScript = null; scriptHandler(script); return; } if (writeCallback) { writeCallback(); } } //Text parsing _setupTokenizerCDATAMode() { const current = this._getAdjustedCurrentElement(); this.tokenizer.allowCDATA = current && current !== this.document && this.treeAdapter.getNamespaceURI(current) !== NS.HTML && !this._isIntegrationPoint(current); } _switchToTextParsing(currentToken, nextTokenizerState) { this._insertElement(currentToken, NS.HTML); this.tokenizer.state = nextTokenizerState; this.originalInsertionMode = this.insertionMode; this.insertionMode = TEXT_MODE; } switchToPlaintextParsing() { this.insertionMode = TEXT_MODE; this.originalInsertionMode = IN_BODY_MODE; this.tokenizer.state = Tokenizer.MODE.PLAINTEXT; } //Fragment parsing _getAdjustedCurrentElement() { return this.openElements.stackTop === 0 && this.fragmentContext ? this.fragmentContext : this.openElements.current; } _findFormInFragmentContext() { let node = this.fragmentContext; do { if (this.treeAdapter.getTagName(node) === $.FORM) { this.formElement = node; break; } node = this.treeAdapter.getParentNode(node); } while (node); } _initTokenizerForFragmentParsing() { if (this.treeAdapter.getNamespaceURI(this.fragmentContext) === NS.HTML) { const tn = this.treeAdapter.getTagName(this.fragmentContext); if (tn === $.TITLE || tn === $.TEXTAREA) { this.tokenizer.state = Tokenizer.MODE.RCDATA; } else if ( tn === $.STYLE || tn === $.XMP || tn === $.IFRAME || tn === $.NOEMBED || tn === $.NOFRAMES || tn === $.NOSCRIPT ) { this.tokenizer.state = Tokenizer.MODE.RAWTEXT; } else if (tn === $.SCRIPT) { this.tokenizer.state = Tokenizer.MODE.SCRIPT_DATA; } else if (tn === $.PLAINTEXT) { this.tokenizer.state = Tokenizer.MODE.PLAINTEXT; } } } //Tree mutation _setDocumentType(token) { const name = token.name || ''; const publicId = token.publicId || ''; const systemId = token.systemId || ''; this.treeAdapter.setDocumentType(this.document, name, publicId, systemId); } _attachElementToTree(element) { if (this._shouldFosterParentOnInsertion()) { this._fosterParentElement(element); } else { const parent = this.openElements.currentTmplContent || this.openElements.current; this.treeAdapter.appendChild(parent, element); } } _appendElement(token, namespaceURI) { const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs); this._attachElementToTree(element); } _insertElement(token, namespaceURI) { const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs); this._attachElementToTree(element); this.openElements.push(element); } _insertFakeElement(tagName) { const element = this.treeAdapter.createElement(tagName, NS.HTML, []); this._attachElementToTree(element); this.openElements.push(element); } _insertTemplate(token) { const tmpl = this.treeAdapter.createElement(token.tagName, NS.HTML, token.attrs); const content = this.treeAdapter.createDocumentFragment(); this.treeAdapter.setTemplateContent(tmpl, content); this._attachElementToTree(tmpl); this.openElements.push(tmpl); } _insertFakeRootElement() { const element = this.treeAdapter.createElement($.HTML, NS.HTML, []); this.treeAdapter.appendChild(this.openElements.current, element); this.openElements.push(element); } _appendCommentNode(token, parent) { const commentNode = this.treeAdapter.createCommentNode(token.data); this.treeAdapter.appendChild(parent, commentNode); } _insertCharacters(token) { if (this._shouldFosterParentOnInsertion()) { this._fosterParentText(token.chars); } else { const parent = this.openElements.currentTmplContent || this.openElements.current; this.treeAdapter.insertText(parent, token.chars); } } _adoptNodes(donor, recipient) { for (let child = this.treeAdapter.getFirstChild(donor); child; child = this.treeAdapter.getFirstChild(donor)) { this.treeAdapter.detachNode(child); this.treeAdapter.appendChild(recipient, child); } } //Token processing _shouldProcessTokenInForeignContent(token) { const current = this._getAdjustedCurrentElement(); if (!current || current === this.document) { return false; } const ns = this.treeAdapter.getNamespaceURI(current); if (ns === NS.HTML) { return false; } if ( this.treeAdapter.getTagName(current) === $.ANNOTATION_XML && ns === NS.MATHML && token.type === Tokenizer.START_TAG_TOKEN && token.tagName === $.SVG ) { return false; } const isCharacterToken = token.type === Tokenizer.CHARACTER_TOKEN || token.type === Tokenizer.NULL_CHARACTER_TOKEN || token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN; const isMathMLTextStartTag = token.type === Tokenizer.START_TAG_TOKEN && token.tagName !== $.MGLYPH && token.tagName !== $.MALIGNMARK; if ((isMathMLTextStartTag || isCharacterToken) && this._isIntegrationPoint(current, NS.MATHML)) { return false; } if ( (token.type === Tokenizer.START_TAG_TOKEN || isCharacterToken) && this._isIntegrationPoint(current, NS.HTML) ) { return false; } return token.type !== Tokenizer.EOF_TOKEN; } _processToken(token) { TOKEN_HANDLERS[this.insertionMode][token.type](this, token); } _processTokenInBodyMode(token) { TOKEN_HANDLERS[IN_BODY_MODE][token.type](this, token); } _processTokenInForeignContent(token) { if (token.type === Tokenizer.CHARACTER_TOKEN) { characterInForeignContent(this, token); } else if (token.type === Tokenizer.NULL_CHARACTER_TOKEN) { nullCharacterInForeignContent(this, token); } else if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN) { insertCharacters(this, token); } else if (token.type === Tokenizer.COMMENT_TOKEN) { appendComment(this, token); } else if (token.type === Tokenizer.START_TAG_TOKEN) { startTagInForeignContent(this, token); } else if (token.type === Tokenizer.END_TAG_TOKEN) { endTagInForeignContent(this, token); } } _processInputToken(token) { if (this._shouldProcessTokenInForeignContent(token)) { this._processTokenInForeignContent(token); } else { this._processToken(token); } if (token.type === Tokenizer.START_TAG_TOKEN && token.selfClosing && !token.ackSelfClosing) { this._err(ERR.nonVoidHtmlElementStartTagWithTrailingSolidus); } } //Integration points _isIntegrationPoint(element, foreignNS) { const tn = this.treeAdapter.getTagName(element); const ns = this.treeAdapter.getNamespaceURI(element); const attrs = this.treeAdapter.getAttrList(element); return foreignContent.isIntegrationPoint(tn, ns, attrs, foreignNS); } //Active formatting elements reconstruction _reconstructActiveFormattingElements() { const listLength = this.activeFormattingElements.length; if (listLength) { let unopenIdx = listLength; let entry = null; do { unopenIdx--; entry = this.activeFormattingElements.entries[unopenIdx]; if (entry.type === FormattingElementList.MARKER_ENTRY || this.openElements.contains(entry.element)) { unopenIdx++; break; } } while (unopenIdx > 0); for (let i = unopenIdx; i < listLength; i++) { entry = this.activeFormattingElements.entries[i]; this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element)); entry.element = this.openElements.current; } } } //Close elements _closeTableCell() { this.openElements.generateImpliedEndTags(); this.openElements.popUntilTableCellPopped(); this.activeFormattingElements.clearToLastMarker(); this.insertionMode = IN_ROW_MODE; } _closePElement() { this.openElements.generateImpliedEndTagsWithExclusion($.P); this.openElements.popUntilTagNamePopped($.P); } //Insertion modes _resetInsertionMode() { for (let i = this.openElements.stackTop, last = false; i >= 0; i--) { let element = this.openElements.items[i]; if (i === 0) { last = true; if (this.fragmentContext) { element = this.fragmentContext; } } const tn = this.treeAdapter.getTagName(element); const newInsertionMode = INSERTION_MODE_RESET_MAP[tn]; if (newInsertionMode) { this.insertionMode = newInsertionMode; break; } else if (!last && (tn === $.TD || tn === $.TH)) { this.insertionMode = IN_CELL_MODE; break; } else if (!last && tn === $.HEAD) { this.insertionMode = IN_HEAD_MODE; break; } else if (tn === $.SELECT) { this._resetInsertionModeForSelect(i); break; } else if (tn === $.TEMPLATE) { this.insertionMode = this.currentTmplInsertionMode; break; } else if (tn === $.HTML) { this.insertionMode = this.headElement ? AFTER_HEAD_MODE : BEFORE_HEAD_MODE; break; } else if (last) { this.insertionMode = IN_BODY_MODE; break; } } } _resetInsertionModeForSelect(selectIdx) { if (selectIdx > 0) { for (let i = selectIdx - 1; i > 0; i--) { const ancestor = this.openElements.items[i]; const tn = this.treeAdapter.getTagName(ancestor); if (tn === $.TEMPLATE) { break; } else if (tn === $.TABLE) { this.insertionMode = IN_SELECT_IN_TABLE_MODE; return; } } } this.insertionMode = IN_SELECT_MODE; } _pushTmplInsertionMode(mode) { this.tmplInsertionModeStack.push(mode); this.tmplInsertionModeStackTop++; this.currentTmplInsertionMode = mode; } _popTmplInsertionMode() { this.tmplInsertionModeStack.pop(); this.tmplInsertionModeStackTop--; this.currentTmplInsertionMode = this.tmplInsertionModeStack[this.tmplInsertionModeStackTop]; } //Foster parenting _isElementCausesFosterParenting(element) { const tn = this.treeAdapter.getTagName(element); return tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR; } _shouldFosterParentOnInsertion() { return this.fosterParentingEnabled && this._isElementCausesFosterParenting(this.openElements.current); } _findFosterParentingLocation() { const location = { parent: null, beforeElement: null }; for (let i = this.openElements.stackTop; i >= 0; i--) { const openElement = this.openElements.items[i]; const tn = this.treeAdapter.getTagName(openElement); const ns = this.treeAdapter.getNamespaceURI(openElement); if (tn === $.TEMPLATE && ns === NS.HTML) { location.parent = this.treeAdapter.getTemplateContent(openElement); break; } else if (tn === $.TABLE) { location.parent = this.treeAdapter.getParentNode(openElement); if (location.parent) { location.beforeElement = openElement; } else { location.parent = this.openElements.items[i - 1]; } break; } } if (!location.parent) { location.parent = this.openElements.items[0]; } return location; } _fosterParentElement(element) { const location = this._findFosterParentingLocation(); if (location.beforeElement) { this.treeAdapter.insertBefore(location.parent, element, location.beforeElement); } else { this.treeAdapter.appendChild(location.parent, element); } } _fosterParentText(chars) { const location = this._findFosterParentingLocation(); if (location.beforeElement) { this.treeAdapter.insertTextBefore(location.parent, chars, location.beforeElement); } else { this.treeAdapter.insertText(location.parent, chars); } } //Special elements _isSpecialElement(element) { const tn = this.treeAdapter.getTagName(element); const ns = this.treeAdapter.getNamespaceURI(element); return HTML.SPECIAL_ELEMENTS[ns][tn]; } } module.exports = Parser; //Adoption agency algorithm //(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency) //------------------------------------------------------------------ //Steps 5-8 of the algorithm function aaObtainFormattingElementEntry(p, token) { let formattingElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(token.tagName); if (formattingElementEntry) { if (!p.openElements.contains(formattingElementEntry.element)) { p.activeFormattingElements.removeEntry(formattingElementEntry); formattingElementEntry = null; } else if (!p.openElements.hasInScope(token.tagName)) { formattingElementEntry = null; } } else { genericEndTagInBody(p, token); } return formattingElementEntry; } //Steps 9 and 10 of the algorithm function aaObtainFurthestBlock(p, formattingElementEntry) { let furthestBlock = null; for (let i = p.openElements.stackTop; i >= 0; i--) { const element = p.openElements.items[i]; if (element === formattingElementEntry.element) { break; } if (p._isSpecialElement(element)) { furthestBlock = element; } } if (!furthestBlock) { p.openElements.popUntilElementPopped(formattingElementEntry.element); p.activeFormattingElements.removeEntry(formattingElementEntry); } return furthestBlock; } //Step 13 of the algorithm function aaInnerLoop(p, furthestBlock, formattingElement) { let lastElement = furthestBlock; let nextElement = p.openElements.getCommonAncestor(furthestBlock); for (let i = 0, element = nextElement; element !== formattingElement; i++, element = nextElement) { //NOTE: store next element for the next loop iteration (it may be deleted from the stack by step 9.5) nextElement = p.openElements.getCommonAncestor(element); const elementEntry = p.activeFormattingElements.getElementEntry(element); const counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER; const shouldRemoveFromOpenElements = !elementEntry || counterOverflow; if (shouldRemoveFromOpenElements) { if (counterOverflow) { p.activeFormattingElements.removeEntry(elementEntry); } p.openElements.remove(element); } else { element = aaRecreateElementFromEntry(p, elementEntry); if (lastElement === furthestBlock) { p.activeFormattingElements.bookmark = elementEntry; } p.treeAdapter.detachNode(lastElement); p.treeAdapter.appendChild(element, lastElement); lastElement = element; } } return lastElement; } //Step 13.7 of the algorithm function aaRecreateElementFromEntry(p, elementEntry) { const ns = p.treeAdapter.getNamespaceURI(elementEntry.element); const newElement = p.treeAdapter.createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs); p.openElements.replace(elementEntry.element, newElement); elementEntry.element = newElement; return newElement; } //Step 14 of the algorithm function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) { if (p._isElementCausesFosterParenting(commonAncestor)) { p._fosterParentElement(lastElement); } else { const tn = p.treeAdapter.getTagName(commonAncestor); const ns = p.treeAdapter.getNamespaceURI(commonAncestor); if (tn === $.TEMPLATE && ns === NS.HTML) { commonAncestor = p.treeAdapter.getTemplateContent(commonAncestor); } p.treeAdapter.appendChild(commonAncestor, lastElement); } } //Steps 15-19 of the algorithm function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) { const ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element); const token = formattingElementEntry.token; const newElement = p.treeAdapter.createElement(token.tagName, ns, token.attrs); p._adoptNodes(furthestBlock, newElement); p.treeAdapter.appendChild(furthestBlock, newElement); p.activeFormattingElements.insertElementAfterBookmark(newElement, formattingElementEntry.token); p.activeFormattingElements.removeEntry(formattingElementEntry); p.openElements.remove(formattingElementEntry.element); p.openElements.insertAfter(furthestBlock, newElement); } //Algorithm entry point function callAdoptionAgency(p, token) { let formattingElementEntry; for (let i = 0; i < AA_OUTER_LOOP_ITER; i++) { formattingElementEntry = aaObtainFormattingElementEntry(p, token, formattingElementEntry); if (!formattingElementEntry) { break; } const furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry); if (!furthestBlock) { break; } p.activeFormattingElements.bookmark = formattingElementEntry; const lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element); const commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element); p.treeAdapter.detachNode(lastElement); aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement); aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry); } } //Generic token handlers //------------------------------------------------------------------ function ignoreToken() { //NOTE: do nothing =) } function misplacedDoctype(p) { p._err(ERR.misplacedDoctype); } function appendComment(p, token) { p._appendCommentNode(token, p.openElements.currentTmplContent || p.openElements.current); } function appendCommentToRootHtmlElement(p, token) { p._appendCommentNode(token, p.openElements.items[0]); } function appendCommentToDocument(p, token) { p._appendCommentNode(token, p.document); } function insertCharacters(p, token) { p._insertCharacters(token); } function stopParsing(p) { p.stopped = true; } // The "initial" insertion mode //------------------------------------------------------------------ function doctypeInInitialMode(p, token) { p._setDocumentType(token); const mode = token.forceQuirks ? HTML.DOCUMENT_MODE.QUIRKS : doctype.getDocumentMode(token); if (!doctype.isConforming(token)) { p._err(ERR.nonConformingDoctype); } p.treeAdapter.setDocumentMode(p.document, mode); p.insertionMode = BEFORE_HTML_MODE; } function tokenInInitialMode(p, token) { p._err(ERR.missingDoctype, { beforeToken: true }); p.treeAdapter.setDocumentMode(p.document, HTML.DOCUMENT_MODE.QUIRKS); p.insertionMode = BEFORE_HTML_MODE; p._processToken(token); } // The "before html" insertion mode //------------------------------------------------------------------ function startTagBeforeHtml(p, token) { if (token.tagName === $.HTML) { p._insertElement(token, NS.HTML); p.insertionMode = BEFORE_HEAD_MODE; } else { tokenBeforeHtml(p, token); } } function endTagBeforeHtml(p, token) { const tn = token.tagName; if (tn === $.HTML || tn === $.HEAD || tn === $.BODY || tn === $.BR) { tokenBeforeHtml(p, token); } } function tokenBeforeHtml(p, token) { p._insertFakeRootElement(); p.insertionMode = BEFORE_HEAD_MODE; p._processToken(token); } // The "before head" insertion mode //------------------------------------------------------------------ function startTagBeforeHead(p, token) { const tn = token.tagName; if (tn === $.HTML) { startTagInBody(p, token); } else if (tn === $.HEAD) { p._insertElement(token, NS.HTML); p.headElement = p.openElements.current; p.insertionMode = IN_HEAD_MODE; } else { tokenBeforeHead(p, token); } } function endTagBeforeHead(p, token) { const tn = token.tagName; if (tn === $.HEAD || tn === $.BODY || tn === $.HTML || tn === $.BR) { tokenBeforeHead(p, token); } else { p._err(ERR.endTagWithoutMatchingOpenElement); } } function tokenBeforeHead(p, token) { p._insertFakeElement($.HEAD); p.headElement = p.openElements.current; p.insertionMode = IN_HEAD_MODE; p._processToken(token); } // The "in head" insertion mode //------------------------------------------------------------------ function startTagInHead(p, token) { const tn = token.tagName; if (tn === $.HTML) { startTagInBody(p, token); } else if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META) { p._appendElement(token, NS.HTML); token.ackSelfClosing = true; } else if (tn === $.TITLE) { p._switchToTextParsing(token, Tokenizer.MODE.RCDATA); } else if (tn === $.NOSCRIPT) { if (p.options.scriptingEnabled) { p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); } else { p._insertElement(token, NS.HTML); p.insertionMode = IN_HEAD_NO_SCRIPT_MODE; } } else if (tn === $.NOFRAMES || tn === $.STYLE) { p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); } else if (tn === $.SCRIPT) { p._switchToTextParsing(token, Tokenizer.MODE.SCRIPT_DATA); } else if (tn === $.TEMPLATE) { p._insertTemplate(token, NS.HTML); p.activeFormattingElements.insertMarker(); p.framesetOk = false; p.insertionMode = IN_TEMPLATE_MODE; p._pushTmplInsertionMode(IN_TEMPLATE_MODE); } else if (tn === $.HEAD) { p._err(ERR.misplacedStartTagForHeadElement); } else { tokenInHead(p, token); } } function endTagInHead(p, token) { const tn = token.tagName; if (tn === $.HEAD) { p.openElements.pop(); p.insertionMode = AFTER_HEAD_MODE; } else if (tn === $.BODY || tn === $.BR || tn === $.HTML) { tokenInHead(p, token); } else if (tn === $.TEMPLATE) { if (p.openElements.tmplCount > 0) { p.openElements.generateImpliedEndTagsThoroughly(); if (p.openElements.currentTagName !== $.TEMPLATE) { p._err(ERR.closingOfElementWithOpenChildElements); } p.openElements.popUntilTagNamePopped($.TEMPLATE); p.activeFormattingElements.clearToLastMarker(); p._popTmplInsertionMode(); p._resetInsertionMode(); } else { p._err(ERR.endTagWithoutMatchingOpenElement); } } else { p._err(ERR.endTagWithoutMatchingOpenElement); } } function tokenInHead(p, token) { p.openElements.pop(); p.insertionMode = AFTER_HEAD_MODE; p._processToken(token); } // The "in head no script" insertion mode //------------------------------------------------------------------ function startTagInHeadNoScript(p, token) { const tn = token.tagName; if (tn === $.HTML) { startTagInBody(p, token); } else if ( tn === $.BASEFONT || tn === $.BGSOUND || tn === $.HEAD || tn === $.LINK || tn === $.META || tn === $.NOFRAMES || tn === $.STYLE ) { startTagInHead(p, token); } else if (tn === $.NOSCRIPT) { p._err(ERR.nestedNoscriptInHead); } else { tokenInHeadNoScript(p, token); } } function endTagInHeadNoScript(p, token) { const tn = token.tagName; if (tn === $.NOSCRIPT) { p.openElements.pop(); p.insertionMode = IN_HEAD_MODE; } else if (tn === $.BR) { tokenInHeadNoScript(p, token); } else { p._err(ERR.endTagWithoutMatchingOpenElement); } } function tokenInHeadNoScript(p, token) { const errCode = token.type === Tokenizer.EOF_TOKEN ? ERR.openElementsLeftAfterEof : ERR.disallowedContentInNoscriptInHead; p._err(errCode); p.openElements.pop(); p.insertionMode = IN_HEAD_MODE; p._processToken(token); } // The "after head" insertion mode //------------------------------------------------------------------ function startTagAfterHead(p, token) { const tn = token.tagName; if (tn === $.HTML) { startTagInBody(p, token); } else if (tn === $.BODY) { p._insertElement(token, NS.HTML); p.framesetOk = false; p.insertionMode = IN_BODY_MODE; } else if (tn === $.FRAMESET) { p._insertElement(token, NS.HTML); p.insertionMode = IN_FRAMESET_MODE; } else if ( tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META || tn === $.NOFRAMES || tn === $.SCRIPT || tn === $.STYLE || tn === $.TEMPLATE || tn === $.TITLE ) { p._err(ERR.abandonedHeadElementChild); p.openElements.push(p.headElement); startTagInHead(p, token); p.openElements.remove(p.headElement); } else if (tn === $.HEAD) { p._err(ERR.misplacedStartTagForHeadElement); } else { tokenAfterHead(p, token); } } function endTagAfterHead(p, token) { const tn = token.tagName; if (tn === $.BODY || tn === $.HTML || tn === $.BR) { tokenAfterHead(p, token); } else if (tn === $.TEMPLATE) { endTagInHead(p, token); } else { p._err(ERR.endTagWithoutMatchingOpenElement); } } function tokenAfterHead(p, token) { p._insertFakeElement($.BODY); p.insertionMode = IN_BODY_MODE; p._processToken(token); } // The "in body" insertion mode //------------------------------------------------------------------ function whitespaceCharacterInBody(p, token) { p._reconstructActiveFormattingElements(); p._insertCharacters(token); } function characterInBody(p, token) { p._reconstructActiveFormattingElements(); p._insertCharacters(token); p.framesetOk = false; } function htmlStartTagInBody(p, token) { if (p.openElements.tmplCount === 0) { p.treeAdapter.adoptAttributes(p.openElements.items[0], token.attrs); } } function bodyStartTagInBody(p, token) { const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement(); if (bodyElement && p.openElements.tmplCount === 0) { p.framesetOk = false; p.treeAdapter.adoptAttributes(bodyElement, token.attrs); } } function framesetStartTagInBody(p, token) { const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement(); if (p.framesetOk && bodyElement) { p.treeAdapter.detachNode(bodyElement); p.openElements.popAllUpToHtmlElement(); p._insertElement(token, NS.HTML); p.insertionMode = IN_FRAMESET_MODE; } } function addressStartTagInBody(p, token) { if (p.openElements.hasInButtonScope($.P)) { p._closePElement(); } p._insertElement(token, NS.HTML); } function numberedHeaderStartTagInBody(p, token) { if (p.openElements.hasInButtonScope($.P)) { p._closePElement(); } const tn = p.openElements.currentTagName; if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) { p.openElements.pop(); } p._insertElement(token, NS.HTML); } function preStartTagInBody(p, token) { if (p.openElements.hasInButtonScope($.P)) { p._closePElement(); } p._insertElement(token, NS.HTML); //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move //on to the next one. (Newlines at the start of pre blocks are ignored as an authoring convenience.) p.skipNextNewLine = true; p.framesetOk = false; } function formStartTagInBody(p, token) { const inTemplate = p.openElements.tmplCount > 0; if (!p.formElement || inTemplate) { if (p.openElements.hasInButtonScope($.P)) { p._closePElement(); } p._insertElement(token, NS.HTML); if (!inTemplate) { p.formElement = p.openElements.current; } } } function listItemStartTagInBody(p, token) { p.framesetOk = false; const tn = token.tagName; for (let i = p.openElements.stackTop; i >= 0; i--) { const element = p.openElements.items[i]; const elementTn = p.treeAdapter.getTagName(element); let closeTn = null; if (tn === $.LI && elementTn === $.LI) { closeTn = $.LI; } else if ((tn === $.DD || tn === $.DT) && (elementTn === $.DD || elementTn === $.DT)) { closeTn = elementTn; } if (closeTn) { p.openElements.generateImpliedEndTagsWithExclusion(closeTn); p.openElements.popUntilTagNamePopped(closeTn); break; } if (elementTn !== $.ADDRESS && elementTn !== $.DIV && elementTn !== $.P && p._isSpecialElement(element)) { break; } } if (p.openElements.hasInButtonScope($.P)) { p._closePElement(); } p._insertElement(token, NS.HTML); } function plaintextStartTagInBody(p, token) { if (p.openElements.hasInButtonScope($.P)) { p._closePElement(); } p._insertElement(token, NS.HTML); p.tokenizer.state = Tokenizer.MODE.PLAINTEXT; } function buttonStartTagInBody(p, token) { if (p.openElements.hasInScope($.BUTTON)) { p.openElements.generateImpliedEndTags(); p.openElements.popUntilTagNamePopped($.BUTTON); } p._reconstructActiveFormattingElements(); p._insertElement(token, NS.HTML); p.framesetOk = false; } function aStartTagInBody(p, token) { const activeElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName($.A); if (activeElementEntry) { callAdoptionAgency(p, token); p.openElements.remove(activeElementEntry.element); p.activeFormattingElements.removeEntry(activeElementEntry); } p._reconstructActiveFormattingElements(); p._insertElement(token, NS.HTML); p.activeFormattingElements.pushElement(p.openElements.curre