index.js 114 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Parser = void 0;
  4. const index_js_1 = require("../tokenizer/index.js");
  5. const open_element_stack_js_1 = require("./open-element-stack.js");
  6. const formatting_element_list_js_1 = require("./formatting-element-list.js");
  7. const default_js_1 = require("../tree-adapters/default.js");
  8. const doctype = require("../common/doctype.js");
  9. const foreignContent = require("../common/foreign-content.js");
  10. const error_codes_js_1 = require("../common/error-codes.js");
  11. const unicode = require("../common/unicode.js");
  12. const html_js_1 = require("../common/html.js");
  13. const token_js_1 = require("../common/token.js");
  14. //Misc constants
  15. const HIDDEN_INPUT_TYPE = 'hidden';
  16. //Adoption agency loops iteration count
  17. const AA_OUTER_LOOP_ITER = 8;
  18. const AA_INNER_LOOP_ITER = 3;
  19. //Insertion modes
  20. var InsertionMode;
  21. (function (InsertionMode) {
  22. InsertionMode[InsertionMode["INITIAL"] = 0] = "INITIAL";
  23. InsertionMode[InsertionMode["BEFORE_HTML"] = 1] = "BEFORE_HTML";
  24. InsertionMode[InsertionMode["BEFORE_HEAD"] = 2] = "BEFORE_HEAD";
  25. InsertionMode[InsertionMode["IN_HEAD"] = 3] = "IN_HEAD";
  26. InsertionMode[InsertionMode["IN_HEAD_NO_SCRIPT"] = 4] = "IN_HEAD_NO_SCRIPT";
  27. InsertionMode[InsertionMode["AFTER_HEAD"] = 5] = "AFTER_HEAD";
  28. InsertionMode[InsertionMode["IN_BODY"] = 6] = "IN_BODY";
  29. InsertionMode[InsertionMode["TEXT"] = 7] = "TEXT";
  30. InsertionMode[InsertionMode["IN_TABLE"] = 8] = "IN_TABLE";
  31. InsertionMode[InsertionMode["IN_TABLE_TEXT"] = 9] = "IN_TABLE_TEXT";
  32. InsertionMode[InsertionMode["IN_CAPTION"] = 10] = "IN_CAPTION";
  33. InsertionMode[InsertionMode["IN_COLUMN_GROUP"] = 11] = "IN_COLUMN_GROUP";
  34. InsertionMode[InsertionMode["IN_TABLE_BODY"] = 12] = "IN_TABLE_BODY";
  35. InsertionMode[InsertionMode["IN_ROW"] = 13] = "IN_ROW";
  36. InsertionMode[InsertionMode["IN_CELL"] = 14] = "IN_CELL";
  37. InsertionMode[InsertionMode["IN_SELECT"] = 15] = "IN_SELECT";
  38. InsertionMode[InsertionMode["IN_SELECT_IN_TABLE"] = 16] = "IN_SELECT_IN_TABLE";
  39. InsertionMode[InsertionMode["IN_TEMPLATE"] = 17] = "IN_TEMPLATE";
  40. InsertionMode[InsertionMode["AFTER_BODY"] = 18] = "AFTER_BODY";
  41. InsertionMode[InsertionMode["IN_FRAMESET"] = 19] = "IN_FRAMESET";
  42. InsertionMode[InsertionMode["AFTER_FRAMESET"] = 20] = "AFTER_FRAMESET";
  43. InsertionMode[InsertionMode["AFTER_AFTER_BODY"] = 21] = "AFTER_AFTER_BODY";
  44. InsertionMode[InsertionMode["AFTER_AFTER_FRAMESET"] = 22] = "AFTER_AFTER_FRAMESET";
  45. })(InsertionMode || (InsertionMode = {}));
  46. const BASE_LOC = {
  47. startLine: -1,
  48. startCol: -1,
  49. startOffset: -1,
  50. endLine: -1,
  51. endCol: -1,
  52. endOffset: -1,
  53. };
  54. const TABLE_STRUCTURE_TAGS = new Set([html_js_1.TAG_ID.TABLE, html_js_1.TAG_ID.TBODY, html_js_1.TAG_ID.TFOOT, html_js_1.TAG_ID.THEAD, html_js_1.TAG_ID.TR]);
  55. const defaultParserOptions = {
  56. scriptingEnabled: true,
  57. sourceCodeLocationInfo: false,
  58. treeAdapter: default_js_1.defaultTreeAdapter,
  59. onParseError: null,
  60. };
  61. //Parser
  62. class Parser {
  63. constructor(options, document,
  64. /** @internal */
  65. fragmentContext = null,
  66. /** @internal */
  67. scriptHandler = null) {
  68. this.fragmentContext = fragmentContext;
  69. this.scriptHandler = scriptHandler;
  70. this.currentToken = null;
  71. this.stopped = false;
  72. /** @internal */
  73. this.insertionMode = InsertionMode.INITIAL;
  74. /** @internal */
  75. this.originalInsertionMode = InsertionMode.INITIAL;
  76. /** @internal */
  77. this.headElement = null;
  78. /** @internal */
  79. this.formElement = null;
  80. /** Indicates that the current node is not an element in the HTML namespace */
  81. this.currentNotInHTML = false;
  82. /**
  83. * The template insertion mode stack is maintained from the left.
  84. * Ie. the topmost element will always have index 0.
  85. *
  86. * @internal
  87. */
  88. this.tmplInsertionModeStack = [];
  89. /** @internal */
  90. this.pendingCharacterTokens = [];
  91. /** @internal */
  92. this.hasNonWhitespacePendingCharacterToken = false;
  93. /** @internal */
  94. this.framesetOk = true;
  95. /** @internal */
  96. this.skipNextNewLine = false;
  97. /** @internal */
  98. this.fosterParentingEnabled = false;
  99. this.options = Object.assign(Object.assign({}, defaultParserOptions), options);
  100. this.treeAdapter = this.options.treeAdapter;
  101. this.onParseError = this.options.onParseError;
  102. // Always enable location info if we report parse errors.
  103. if (this.onParseError) {
  104. this.options.sourceCodeLocationInfo = true;
  105. }
  106. this.document = document !== null && document !== void 0 ? document : this.treeAdapter.createDocument();
  107. this.tokenizer = new index_js_1.Tokenizer(this.options, this);
  108. this.activeFormattingElements = new formatting_element_list_js_1.FormattingElementList(this.treeAdapter);
  109. this.fragmentContextID = fragmentContext ? (0, html_js_1.getTagID)(this.treeAdapter.getTagName(fragmentContext)) : html_js_1.TAG_ID.UNKNOWN;
  110. this._setContextModes(fragmentContext !== null && fragmentContext !== void 0 ? fragmentContext : this.document, this.fragmentContextID);
  111. this.openElements = new open_element_stack_js_1.OpenElementStack(this.document, this.treeAdapter, this);
  112. }
  113. // API
  114. static parse(html, options) {
  115. const parser = new this(options);
  116. parser.tokenizer.write(html, true);
  117. return parser.document;
  118. }
  119. static getFragmentParser(fragmentContext, options) {
  120. const opts = Object.assign(Object.assign({}, defaultParserOptions), options);
  121. //NOTE: use a <template> element as the fragment context if no context element was provided,
  122. //so we will parse in a "forgiving" manner
  123. fragmentContext !== null && fragmentContext !== void 0 ? fragmentContext : (fragmentContext = opts.treeAdapter.createElement(html_js_1.TAG_NAMES.TEMPLATE, html_js_1.NS.HTML, []));
  124. //NOTE: create a fake element which will be used as the `document` for fragment parsing.
  125. //This is important for jsdom, where a new `document` cannot be created. This led to
  126. //fragment parsing messing with the main `document`.
  127. const documentMock = opts.treeAdapter.createElement('documentmock', html_js_1.NS.HTML, []);
  128. const parser = new this(opts, documentMock, fragmentContext);
  129. if (parser.fragmentContextID === html_js_1.TAG_ID.TEMPLATE) {
  130. parser.tmplInsertionModeStack.unshift(InsertionMode.IN_TEMPLATE);
  131. }
  132. parser._initTokenizerForFragmentParsing();
  133. parser._insertFakeRootElement();
  134. parser._resetInsertionMode();
  135. parser._findFormInFragmentContext();
  136. return parser;
  137. }
  138. getFragment() {
  139. const rootElement = this.treeAdapter.getFirstChild(this.document);
  140. const fragment = this.treeAdapter.createDocumentFragment();
  141. this._adoptNodes(rootElement, fragment);
  142. return fragment;
  143. }
  144. //Errors
  145. /** @internal */
  146. _err(token, code, beforeToken) {
  147. var _a;
  148. if (!this.onParseError)
  149. return;
  150. const loc = (_a = token.location) !== null && _a !== void 0 ? _a : BASE_LOC;
  151. const err = {
  152. code,
  153. startLine: loc.startLine,
  154. startCol: loc.startCol,
  155. startOffset: loc.startOffset,
  156. endLine: beforeToken ? loc.startLine : loc.endLine,
  157. endCol: beforeToken ? loc.startCol : loc.endCol,
  158. endOffset: beforeToken ? loc.startOffset : loc.endOffset,
  159. };
  160. this.onParseError(err);
  161. }
  162. //Stack events
  163. /** @internal */
  164. onItemPush(node, tid, isTop) {
  165. var _a, _b;
  166. (_b = (_a = this.treeAdapter).onItemPush) === null || _b === void 0 ? void 0 : _b.call(_a, node);
  167. if (isTop && this.openElements.stackTop > 0)
  168. this._setContextModes(node, tid);
  169. }
  170. /** @internal */
  171. onItemPop(node, isTop) {
  172. var _a, _b;
  173. if (this.options.sourceCodeLocationInfo) {
  174. this._setEndLocation(node, this.currentToken);
  175. }
  176. (_b = (_a = this.treeAdapter).onItemPop) === null || _b === void 0 ? void 0 : _b.call(_a, node, this.openElements.current);
  177. if (isTop) {
  178. let current;
  179. let currentTagId;
  180. if (this.openElements.stackTop === 0 && this.fragmentContext) {
  181. current = this.fragmentContext;
  182. currentTagId = this.fragmentContextID;
  183. }
  184. else {
  185. ({ current, currentTagId } = this.openElements);
  186. }
  187. this._setContextModes(current, currentTagId);
  188. }
  189. }
  190. _setContextModes(current, tid) {
  191. const isHTML = current === this.document || this.treeAdapter.getNamespaceURI(current) === html_js_1.NS.HTML;
  192. this.currentNotInHTML = !isHTML;
  193. this.tokenizer.inForeignNode = !isHTML && !this._isIntegrationPoint(tid, current);
  194. }
  195. /** @protected */
  196. _switchToTextParsing(currentToken, nextTokenizerState) {
  197. this._insertElement(currentToken, html_js_1.NS.HTML);
  198. this.tokenizer.state = nextTokenizerState;
  199. this.originalInsertionMode = this.insertionMode;
  200. this.insertionMode = InsertionMode.TEXT;
  201. }
  202. switchToPlaintextParsing() {
  203. this.insertionMode = InsertionMode.TEXT;
  204. this.originalInsertionMode = InsertionMode.IN_BODY;
  205. this.tokenizer.state = index_js_1.TokenizerMode.PLAINTEXT;
  206. }
  207. //Fragment parsing
  208. /** @protected */
  209. _getAdjustedCurrentElement() {
  210. return this.openElements.stackTop === 0 && this.fragmentContext
  211. ? this.fragmentContext
  212. : this.openElements.current;
  213. }
  214. /** @protected */
  215. _findFormInFragmentContext() {
  216. let node = this.fragmentContext;
  217. while (node) {
  218. if (this.treeAdapter.getTagName(node) === html_js_1.TAG_NAMES.FORM) {
  219. this.formElement = node;
  220. break;
  221. }
  222. node = this.treeAdapter.getParentNode(node);
  223. }
  224. }
  225. _initTokenizerForFragmentParsing() {
  226. if (!this.fragmentContext || this.treeAdapter.getNamespaceURI(this.fragmentContext) !== html_js_1.NS.HTML) {
  227. return;
  228. }
  229. switch (this.fragmentContextID) {
  230. case html_js_1.TAG_ID.TITLE:
  231. case html_js_1.TAG_ID.TEXTAREA: {
  232. this.tokenizer.state = index_js_1.TokenizerMode.RCDATA;
  233. break;
  234. }
  235. case html_js_1.TAG_ID.STYLE:
  236. case html_js_1.TAG_ID.XMP:
  237. case html_js_1.TAG_ID.IFRAME:
  238. case html_js_1.TAG_ID.NOEMBED:
  239. case html_js_1.TAG_ID.NOFRAMES:
  240. case html_js_1.TAG_ID.NOSCRIPT: {
  241. this.tokenizer.state = index_js_1.TokenizerMode.RAWTEXT;
  242. break;
  243. }
  244. case html_js_1.TAG_ID.SCRIPT: {
  245. this.tokenizer.state = index_js_1.TokenizerMode.SCRIPT_DATA;
  246. break;
  247. }
  248. case html_js_1.TAG_ID.PLAINTEXT: {
  249. this.tokenizer.state = index_js_1.TokenizerMode.PLAINTEXT;
  250. break;
  251. }
  252. default:
  253. // Do nothing
  254. }
  255. }
  256. //Tree mutation
  257. /** @protected */
  258. _setDocumentType(token) {
  259. const name = token.name || '';
  260. const publicId = token.publicId || '';
  261. const systemId = token.systemId || '';
  262. this.treeAdapter.setDocumentType(this.document, name, publicId, systemId);
  263. if (token.location) {
  264. const documentChildren = this.treeAdapter.getChildNodes(this.document);
  265. const docTypeNode = documentChildren.find((node) => this.treeAdapter.isDocumentTypeNode(node));
  266. if (docTypeNode) {
  267. this.treeAdapter.setNodeSourceCodeLocation(docTypeNode, token.location);
  268. }
  269. }
  270. }
  271. /** @protected */
  272. _attachElementToTree(element, location) {
  273. if (this.options.sourceCodeLocationInfo) {
  274. const loc = location && Object.assign(Object.assign({}, location), { startTag: location });
  275. this.treeAdapter.setNodeSourceCodeLocation(element, loc);
  276. }
  277. if (this._shouldFosterParentOnInsertion()) {
  278. this._fosterParentElement(element);
  279. }
  280. else {
  281. const parent = this.openElements.currentTmplContentOrNode;
  282. this.treeAdapter.appendChild(parent, element);
  283. }
  284. }
  285. /**
  286. * For self-closing tags. Add an element to the tree, but skip adding it
  287. * to the stack.
  288. */
  289. /** @protected */
  290. _appendElement(token, namespaceURI) {
  291. const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
  292. this._attachElementToTree(element, token.location);
  293. }
  294. /** @protected */
  295. _insertElement(token, namespaceURI) {
  296. const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
  297. this._attachElementToTree(element, token.location);
  298. this.openElements.push(element, token.tagID);
  299. }
  300. /** @protected */
  301. _insertFakeElement(tagName, tagID) {
  302. const element = this.treeAdapter.createElement(tagName, html_js_1.NS.HTML, []);
  303. this._attachElementToTree(element, null);
  304. this.openElements.push(element, tagID);
  305. }
  306. /** @protected */
  307. _insertTemplate(token) {
  308. const tmpl = this.treeAdapter.createElement(token.tagName, html_js_1.NS.HTML, token.attrs);
  309. const content = this.treeAdapter.createDocumentFragment();
  310. this.treeAdapter.setTemplateContent(tmpl, content);
  311. this._attachElementToTree(tmpl, token.location);
  312. this.openElements.push(tmpl, token.tagID);
  313. if (this.options.sourceCodeLocationInfo)
  314. this.treeAdapter.setNodeSourceCodeLocation(content, null);
  315. }
  316. /** @protected */
  317. _insertFakeRootElement() {
  318. const element = this.treeAdapter.createElement(html_js_1.TAG_NAMES.HTML, html_js_1.NS.HTML, []);
  319. if (this.options.sourceCodeLocationInfo)
  320. this.treeAdapter.setNodeSourceCodeLocation(element, null);
  321. this.treeAdapter.appendChild(this.openElements.current, element);
  322. this.openElements.push(element, html_js_1.TAG_ID.HTML);
  323. }
  324. /** @protected */
  325. _appendCommentNode(token, parent) {
  326. const commentNode = this.treeAdapter.createCommentNode(token.data);
  327. this.treeAdapter.appendChild(parent, commentNode);
  328. if (this.options.sourceCodeLocationInfo) {
  329. this.treeAdapter.setNodeSourceCodeLocation(commentNode, token.location);
  330. }
  331. }
  332. /** @protected */
  333. _insertCharacters(token) {
  334. let parent;
  335. let beforeElement;
  336. if (this._shouldFosterParentOnInsertion()) {
  337. ({ parent, beforeElement } = this._findFosterParentingLocation());
  338. if (beforeElement) {
  339. this.treeAdapter.insertTextBefore(parent, token.chars, beforeElement);
  340. }
  341. else {
  342. this.treeAdapter.insertText(parent, token.chars);
  343. }
  344. }
  345. else {
  346. parent = this.openElements.currentTmplContentOrNode;
  347. this.treeAdapter.insertText(parent, token.chars);
  348. }
  349. if (!token.location)
  350. return;
  351. const siblings = this.treeAdapter.getChildNodes(parent);
  352. const textNodeIdx = beforeElement ? siblings.lastIndexOf(beforeElement) : siblings.length;
  353. const textNode = siblings[textNodeIdx - 1];
  354. //NOTE: if we have a location assigned by another token, then just update the end position
  355. const tnLoc = this.treeAdapter.getNodeSourceCodeLocation(textNode);
  356. if (tnLoc) {
  357. const { endLine, endCol, endOffset } = token.location;
  358. this.treeAdapter.updateNodeSourceCodeLocation(textNode, { endLine, endCol, endOffset });
  359. }
  360. else if (this.options.sourceCodeLocationInfo) {
  361. this.treeAdapter.setNodeSourceCodeLocation(textNode, token.location);
  362. }
  363. }
  364. /** @protected */
  365. _adoptNodes(donor, recipient) {
  366. for (let child = this.treeAdapter.getFirstChild(donor); child; child = this.treeAdapter.getFirstChild(donor)) {
  367. this.treeAdapter.detachNode(child);
  368. this.treeAdapter.appendChild(recipient, child);
  369. }
  370. }
  371. /** @protected */
  372. _setEndLocation(element, closingToken) {
  373. if (this.treeAdapter.getNodeSourceCodeLocation(element) && closingToken.location) {
  374. const ctLoc = closingToken.location;
  375. const tn = this.treeAdapter.getTagName(element);
  376. const endLoc =
  377. // NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing
  378. // tag and for cases like <td> <p> </td> - 'p' closes without a closing tag.
  379. closingToken.type === token_js_1.TokenType.END_TAG && tn === closingToken.tagName
  380. ? {
  381. endTag: Object.assign({}, ctLoc),
  382. endLine: ctLoc.endLine,
  383. endCol: ctLoc.endCol,
  384. endOffset: ctLoc.endOffset,
  385. }
  386. : {
  387. endLine: ctLoc.startLine,
  388. endCol: ctLoc.startCol,
  389. endOffset: ctLoc.startOffset,
  390. };
  391. this.treeAdapter.updateNodeSourceCodeLocation(element, endLoc);
  392. }
  393. }
  394. //Token processing
  395. shouldProcessStartTagTokenInForeignContent(token) {
  396. // Check that neither current === document, or ns === NS.HTML
  397. if (!this.currentNotInHTML)
  398. return false;
  399. let current;
  400. let currentTagId;
  401. if (this.openElements.stackTop === 0 && this.fragmentContext) {
  402. current = this.fragmentContext;
  403. currentTagId = this.fragmentContextID;
  404. }
  405. else {
  406. ({ current, currentTagId } = this.openElements);
  407. }
  408. if (token.tagID === html_js_1.TAG_ID.SVG &&
  409. this.treeAdapter.getTagName(current) === html_js_1.TAG_NAMES.ANNOTATION_XML &&
  410. this.treeAdapter.getNamespaceURI(current) === html_js_1.NS.MATHML) {
  411. return false;
  412. }
  413. return (
  414. // Check that `current` is not an integration point for HTML or MathML elements.
  415. this.tokenizer.inForeignNode ||
  416. // If it _is_ an integration point, then we might have to check that it is not an HTML
  417. // integration point.
  418. ((token.tagID === html_js_1.TAG_ID.MGLYPH || token.tagID === html_js_1.TAG_ID.MALIGNMARK) &&
  419. !this._isIntegrationPoint(currentTagId, current, html_js_1.NS.HTML)));
  420. }
  421. /** @protected */
  422. _processToken(token) {
  423. switch (token.type) {
  424. case token_js_1.TokenType.CHARACTER: {
  425. this.onCharacter(token);
  426. break;
  427. }
  428. case token_js_1.TokenType.NULL_CHARACTER: {
  429. this.onNullCharacter(token);
  430. break;
  431. }
  432. case token_js_1.TokenType.COMMENT: {
  433. this.onComment(token);
  434. break;
  435. }
  436. case token_js_1.TokenType.DOCTYPE: {
  437. this.onDoctype(token);
  438. break;
  439. }
  440. case token_js_1.TokenType.START_TAG: {
  441. this._processStartTag(token);
  442. break;
  443. }
  444. case token_js_1.TokenType.END_TAG: {
  445. this.onEndTag(token);
  446. break;
  447. }
  448. case token_js_1.TokenType.EOF: {
  449. this.onEof(token);
  450. break;
  451. }
  452. case token_js_1.TokenType.WHITESPACE_CHARACTER: {
  453. this.onWhitespaceCharacter(token);
  454. break;
  455. }
  456. }
  457. }
  458. //Integration points
  459. /** @protected */
  460. _isIntegrationPoint(tid, element, foreignNS) {
  461. const ns = this.treeAdapter.getNamespaceURI(element);
  462. const attrs = this.treeAdapter.getAttrList(element);
  463. return foreignContent.isIntegrationPoint(tid, ns, attrs, foreignNS);
  464. }
  465. //Active formatting elements reconstruction
  466. /** @protected */
  467. _reconstructActiveFormattingElements() {
  468. const listLength = this.activeFormattingElements.entries.length;
  469. if (listLength) {
  470. const endIndex = this.activeFormattingElements.entries.findIndex((entry) => entry.type === formatting_element_list_js_1.EntryType.Marker || this.openElements.contains(entry.element));
  471. const unopenIdx = endIndex < 0 ? listLength - 1 : endIndex - 1;
  472. for (let i = unopenIdx; i >= 0; i--) {
  473. const entry = this.activeFormattingElements.entries[i];
  474. this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element));
  475. entry.element = this.openElements.current;
  476. }
  477. }
  478. }
  479. //Close elements
  480. /** @protected */
  481. _closeTableCell() {
  482. this.openElements.generateImpliedEndTags();
  483. this.openElements.popUntilTableCellPopped();
  484. this.activeFormattingElements.clearToLastMarker();
  485. this.insertionMode = InsertionMode.IN_ROW;
  486. }
  487. /** @protected */
  488. _closePElement() {
  489. this.openElements.generateImpliedEndTagsWithExclusion(html_js_1.TAG_ID.P);
  490. this.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.P);
  491. }
  492. //Insertion modes
  493. /** @protected */
  494. _resetInsertionMode() {
  495. for (let i = this.openElements.stackTop; i >= 0; i--) {
  496. //Insertion mode reset map
  497. switch (i === 0 && this.fragmentContext ? this.fragmentContextID : this.openElements.tagIDs[i]) {
  498. case html_js_1.TAG_ID.TR: {
  499. this.insertionMode = InsertionMode.IN_ROW;
  500. return;
  501. }
  502. case html_js_1.TAG_ID.TBODY:
  503. case html_js_1.TAG_ID.THEAD:
  504. case html_js_1.TAG_ID.TFOOT: {
  505. this.insertionMode = InsertionMode.IN_TABLE_BODY;
  506. return;
  507. }
  508. case html_js_1.TAG_ID.CAPTION: {
  509. this.insertionMode = InsertionMode.IN_CAPTION;
  510. return;
  511. }
  512. case html_js_1.TAG_ID.COLGROUP: {
  513. this.insertionMode = InsertionMode.IN_COLUMN_GROUP;
  514. return;
  515. }
  516. case html_js_1.TAG_ID.TABLE: {
  517. this.insertionMode = InsertionMode.IN_TABLE;
  518. return;
  519. }
  520. case html_js_1.TAG_ID.BODY: {
  521. this.insertionMode = InsertionMode.IN_BODY;
  522. return;
  523. }
  524. case html_js_1.TAG_ID.FRAMESET: {
  525. this.insertionMode = InsertionMode.IN_FRAMESET;
  526. return;
  527. }
  528. case html_js_1.TAG_ID.SELECT: {
  529. this._resetInsertionModeForSelect(i);
  530. return;
  531. }
  532. case html_js_1.TAG_ID.TEMPLATE: {
  533. this.insertionMode = this.tmplInsertionModeStack[0];
  534. return;
  535. }
  536. case html_js_1.TAG_ID.HTML: {
  537. this.insertionMode = this.headElement ? InsertionMode.AFTER_HEAD : InsertionMode.BEFORE_HEAD;
  538. return;
  539. }
  540. case html_js_1.TAG_ID.TD:
  541. case html_js_1.TAG_ID.TH: {
  542. if (i > 0) {
  543. this.insertionMode = InsertionMode.IN_CELL;
  544. return;
  545. }
  546. break;
  547. }
  548. case html_js_1.TAG_ID.HEAD: {
  549. if (i > 0) {
  550. this.insertionMode = InsertionMode.IN_HEAD;
  551. return;
  552. }
  553. break;
  554. }
  555. }
  556. }
  557. this.insertionMode = InsertionMode.IN_BODY;
  558. }
  559. /** @protected */
  560. _resetInsertionModeForSelect(selectIdx) {
  561. if (selectIdx > 0) {
  562. for (let i = selectIdx - 1; i > 0; i--) {
  563. const tn = this.openElements.tagIDs[i];
  564. if (tn === html_js_1.TAG_ID.TEMPLATE) {
  565. break;
  566. }
  567. else if (tn === html_js_1.TAG_ID.TABLE) {
  568. this.insertionMode = InsertionMode.IN_SELECT_IN_TABLE;
  569. return;
  570. }
  571. }
  572. }
  573. this.insertionMode = InsertionMode.IN_SELECT;
  574. }
  575. //Foster parenting
  576. /** @protected */
  577. _isElementCausesFosterParenting(tn) {
  578. return TABLE_STRUCTURE_TAGS.has(tn);
  579. }
  580. /** @protected */
  581. _shouldFosterParentOnInsertion() {
  582. return this.fosterParentingEnabled && this._isElementCausesFosterParenting(this.openElements.currentTagId);
  583. }
  584. /** @protected */
  585. _findFosterParentingLocation() {
  586. for (let i = this.openElements.stackTop; i >= 0; i--) {
  587. const openElement = this.openElements.items[i];
  588. switch (this.openElements.tagIDs[i]) {
  589. case html_js_1.TAG_ID.TEMPLATE: {
  590. if (this.treeAdapter.getNamespaceURI(openElement) === html_js_1.NS.HTML) {
  591. return { parent: this.treeAdapter.getTemplateContent(openElement), beforeElement: null };
  592. }
  593. break;
  594. }
  595. case html_js_1.TAG_ID.TABLE: {
  596. const parent = this.treeAdapter.getParentNode(openElement);
  597. if (parent) {
  598. return { parent, beforeElement: openElement };
  599. }
  600. return { parent: this.openElements.items[i - 1], beforeElement: null };
  601. }
  602. default:
  603. // Do nothing
  604. }
  605. }
  606. return { parent: this.openElements.items[0], beforeElement: null };
  607. }
  608. /** @protected */
  609. _fosterParentElement(element) {
  610. const location = this._findFosterParentingLocation();
  611. if (location.beforeElement) {
  612. this.treeAdapter.insertBefore(location.parent, element, location.beforeElement);
  613. }
  614. else {
  615. this.treeAdapter.appendChild(location.parent, element);
  616. }
  617. }
  618. //Special elements
  619. /** @protected */
  620. _isSpecialElement(element, id) {
  621. const ns = this.treeAdapter.getNamespaceURI(element);
  622. return html_js_1.SPECIAL_ELEMENTS[ns].has(id);
  623. }
  624. /** @internal */
  625. onCharacter(token) {
  626. this.skipNextNewLine = false;
  627. if (this.tokenizer.inForeignNode) {
  628. characterInForeignContent(this, token);
  629. return;
  630. }
  631. switch (this.insertionMode) {
  632. case InsertionMode.INITIAL: {
  633. tokenInInitialMode(this, token);
  634. break;
  635. }
  636. case InsertionMode.BEFORE_HTML: {
  637. tokenBeforeHtml(this, token);
  638. break;
  639. }
  640. case InsertionMode.BEFORE_HEAD: {
  641. tokenBeforeHead(this, token);
  642. break;
  643. }
  644. case InsertionMode.IN_HEAD: {
  645. tokenInHead(this, token);
  646. break;
  647. }
  648. case InsertionMode.IN_HEAD_NO_SCRIPT: {
  649. tokenInHeadNoScript(this, token);
  650. break;
  651. }
  652. case InsertionMode.AFTER_HEAD: {
  653. tokenAfterHead(this, token);
  654. break;
  655. }
  656. case InsertionMode.IN_BODY:
  657. case InsertionMode.IN_CAPTION:
  658. case InsertionMode.IN_CELL:
  659. case InsertionMode.IN_TEMPLATE: {
  660. characterInBody(this, token);
  661. break;
  662. }
  663. case InsertionMode.TEXT:
  664. case InsertionMode.IN_SELECT:
  665. case InsertionMode.IN_SELECT_IN_TABLE: {
  666. this._insertCharacters(token);
  667. break;
  668. }
  669. case InsertionMode.IN_TABLE:
  670. case InsertionMode.IN_TABLE_BODY:
  671. case InsertionMode.IN_ROW: {
  672. characterInTable(this, token);
  673. break;
  674. }
  675. case InsertionMode.IN_TABLE_TEXT: {
  676. characterInTableText(this, token);
  677. break;
  678. }
  679. case InsertionMode.IN_COLUMN_GROUP: {
  680. tokenInColumnGroup(this, token);
  681. break;
  682. }
  683. case InsertionMode.AFTER_BODY: {
  684. tokenAfterBody(this, token);
  685. break;
  686. }
  687. case InsertionMode.AFTER_AFTER_BODY: {
  688. tokenAfterAfterBody(this, token);
  689. break;
  690. }
  691. default:
  692. // Do nothing
  693. }
  694. }
  695. /** @internal */
  696. onNullCharacter(token) {
  697. this.skipNextNewLine = false;
  698. if (this.tokenizer.inForeignNode) {
  699. nullCharacterInForeignContent(this, token);
  700. return;
  701. }
  702. switch (this.insertionMode) {
  703. case InsertionMode.INITIAL: {
  704. tokenInInitialMode(this, token);
  705. break;
  706. }
  707. case InsertionMode.BEFORE_HTML: {
  708. tokenBeforeHtml(this, token);
  709. break;
  710. }
  711. case InsertionMode.BEFORE_HEAD: {
  712. tokenBeforeHead(this, token);
  713. break;
  714. }
  715. case InsertionMode.IN_HEAD: {
  716. tokenInHead(this, token);
  717. break;
  718. }
  719. case InsertionMode.IN_HEAD_NO_SCRIPT: {
  720. tokenInHeadNoScript(this, token);
  721. break;
  722. }
  723. case InsertionMode.AFTER_HEAD: {
  724. tokenAfterHead(this, token);
  725. break;
  726. }
  727. case InsertionMode.TEXT: {
  728. this._insertCharacters(token);
  729. break;
  730. }
  731. case InsertionMode.IN_TABLE:
  732. case InsertionMode.IN_TABLE_BODY:
  733. case InsertionMode.IN_ROW: {
  734. characterInTable(this, token);
  735. break;
  736. }
  737. case InsertionMode.IN_COLUMN_GROUP: {
  738. tokenInColumnGroup(this, token);
  739. break;
  740. }
  741. case InsertionMode.AFTER_BODY: {
  742. tokenAfterBody(this, token);
  743. break;
  744. }
  745. case InsertionMode.AFTER_AFTER_BODY: {
  746. tokenAfterAfterBody(this, token);
  747. break;
  748. }
  749. default:
  750. // Do nothing
  751. }
  752. }
  753. /** @internal */
  754. onComment(token) {
  755. this.skipNextNewLine = false;
  756. if (this.currentNotInHTML) {
  757. appendComment(this, token);
  758. return;
  759. }
  760. switch (this.insertionMode) {
  761. case InsertionMode.INITIAL:
  762. case InsertionMode.BEFORE_HTML:
  763. case InsertionMode.BEFORE_HEAD:
  764. case InsertionMode.IN_HEAD:
  765. case InsertionMode.IN_HEAD_NO_SCRIPT:
  766. case InsertionMode.AFTER_HEAD:
  767. case InsertionMode.IN_BODY:
  768. case InsertionMode.IN_TABLE:
  769. case InsertionMode.IN_CAPTION:
  770. case InsertionMode.IN_COLUMN_GROUP:
  771. case InsertionMode.IN_TABLE_BODY:
  772. case InsertionMode.IN_ROW:
  773. case InsertionMode.IN_CELL:
  774. case InsertionMode.IN_SELECT:
  775. case InsertionMode.IN_SELECT_IN_TABLE:
  776. case InsertionMode.IN_TEMPLATE:
  777. case InsertionMode.IN_FRAMESET:
  778. case InsertionMode.AFTER_FRAMESET: {
  779. appendComment(this, token);
  780. break;
  781. }
  782. case InsertionMode.IN_TABLE_TEXT: {
  783. tokenInTableText(this, token);
  784. break;
  785. }
  786. case InsertionMode.AFTER_BODY: {
  787. appendCommentToRootHtmlElement(this, token);
  788. break;
  789. }
  790. case InsertionMode.AFTER_AFTER_BODY:
  791. case InsertionMode.AFTER_AFTER_FRAMESET: {
  792. appendCommentToDocument(this, token);
  793. break;
  794. }
  795. default:
  796. // Do nothing
  797. }
  798. }
  799. /** @internal */
  800. onDoctype(token) {
  801. this.skipNextNewLine = false;
  802. switch (this.insertionMode) {
  803. case InsertionMode.INITIAL: {
  804. doctypeInInitialMode(this, token);
  805. break;
  806. }
  807. case InsertionMode.BEFORE_HEAD:
  808. case InsertionMode.IN_HEAD:
  809. case InsertionMode.IN_HEAD_NO_SCRIPT:
  810. case InsertionMode.AFTER_HEAD: {
  811. this._err(token, error_codes_js_1.ERR.misplacedDoctype);
  812. break;
  813. }
  814. case InsertionMode.IN_TABLE_TEXT: {
  815. tokenInTableText(this, token);
  816. break;
  817. }
  818. default:
  819. // Do nothing
  820. }
  821. }
  822. /** @internal */
  823. onStartTag(token) {
  824. this.skipNextNewLine = false;
  825. this.currentToken = token;
  826. this._processStartTag(token);
  827. if (token.selfClosing && !token.ackSelfClosing) {
  828. this._err(token, error_codes_js_1.ERR.nonVoidHtmlElementStartTagWithTrailingSolidus);
  829. }
  830. }
  831. /**
  832. * Processes a given start tag.
  833. *
  834. * `onStartTag` checks if a self-closing tag was recognized. When a token
  835. * is moved inbetween multiple insertion modes, this check for self-closing
  836. * could lead to false positives. To avoid this, `_processStartTag` is used
  837. * for nested calls.
  838. *
  839. * @param token The token to process.
  840. * @protected
  841. */
  842. _processStartTag(token) {
  843. if (this.shouldProcessStartTagTokenInForeignContent(token)) {
  844. startTagInForeignContent(this, token);
  845. }
  846. else {
  847. this._startTagOutsideForeignContent(token);
  848. }
  849. }
  850. /** @protected */
  851. _startTagOutsideForeignContent(token) {
  852. switch (this.insertionMode) {
  853. case InsertionMode.INITIAL: {
  854. tokenInInitialMode(this, token);
  855. break;
  856. }
  857. case InsertionMode.BEFORE_HTML: {
  858. startTagBeforeHtml(this, token);
  859. break;
  860. }
  861. case InsertionMode.BEFORE_HEAD: {
  862. startTagBeforeHead(this, token);
  863. break;
  864. }
  865. case InsertionMode.IN_HEAD: {
  866. startTagInHead(this, token);
  867. break;
  868. }
  869. case InsertionMode.IN_HEAD_NO_SCRIPT: {
  870. startTagInHeadNoScript(this, token);
  871. break;
  872. }
  873. case InsertionMode.AFTER_HEAD: {
  874. startTagAfterHead(this, token);
  875. break;
  876. }
  877. case InsertionMode.IN_BODY: {
  878. startTagInBody(this, token);
  879. break;
  880. }
  881. case InsertionMode.IN_TABLE: {
  882. startTagInTable(this, token);
  883. break;
  884. }
  885. case InsertionMode.IN_TABLE_TEXT: {
  886. tokenInTableText(this, token);
  887. break;
  888. }
  889. case InsertionMode.IN_CAPTION: {
  890. startTagInCaption(this, token);
  891. break;
  892. }
  893. case InsertionMode.IN_COLUMN_GROUP: {
  894. startTagInColumnGroup(this, token);
  895. break;
  896. }
  897. case InsertionMode.IN_TABLE_BODY: {
  898. startTagInTableBody(this, token);
  899. break;
  900. }
  901. case InsertionMode.IN_ROW: {
  902. startTagInRow(this, token);
  903. break;
  904. }
  905. case InsertionMode.IN_CELL: {
  906. startTagInCell(this, token);
  907. break;
  908. }
  909. case InsertionMode.IN_SELECT: {
  910. startTagInSelect(this, token);
  911. break;
  912. }
  913. case InsertionMode.IN_SELECT_IN_TABLE: {
  914. startTagInSelectInTable(this, token);
  915. break;
  916. }
  917. case InsertionMode.IN_TEMPLATE: {
  918. startTagInTemplate(this, token);
  919. break;
  920. }
  921. case InsertionMode.AFTER_BODY: {
  922. startTagAfterBody(this, token);
  923. break;
  924. }
  925. case InsertionMode.IN_FRAMESET: {
  926. startTagInFrameset(this, token);
  927. break;
  928. }
  929. case InsertionMode.AFTER_FRAMESET: {
  930. startTagAfterFrameset(this, token);
  931. break;
  932. }
  933. case InsertionMode.AFTER_AFTER_BODY: {
  934. startTagAfterAfterBody(this, token);
  935. break;
  936. }
  937. case InsertionMode.AFTER_AFTER_FRAMESET: {
  938. startTagAfterAfterFrameset(this, token);
  939. break;
  940. }
  941. default:
  942. // Do nothing
  943. }
  944. }
  945. /** @internal */
  946. onEndTag(token) {
  947. this.skipNextNewLine = false;
  948. this.currentToken = token;
  949. if (this.currentNotInHTML) {
  950. endTagInForeignContent(this, token);
  951. }
  952. else {
  953. this._endTagOutsideForeignContent(token);
  954. }
  955. }
  956. /** @protected */
  957. _endTagOutsideForeignContent(token) {
  958. switch (this.insertionMode) {
  959. case InsertionMode.INITIAL: {
  960. tokenInInitialMode(this, token);
  961. break;
  962. }
  963. case InsertionMode.BEFORE_HTML: {
  964. endTagBeforeHtml(this, token);
  965. break;
  966. }
  967. case InsertionMode.BEFORE_HEAD: {
  968. endTagBeforeHead(this, token);
  969. break;
  970. }
  971. case InsertionMode.IN_HEAD: {
  972. endTagInHead(this, token);
  973. break;
  974. }
  975. case InsertionMode.IN_HEAD_NO_SCRIPT: {
  976. endTagInHeadNoScript(this, token);
  977. break;
  978. }
  979. case InsertionMode.AFTER_HEAD: {
  980. endTagAfterHead(this, token);
  981. break;
  982. }
  983. case InsertionMode.IN_BODY: {
  984. endTagInBody(this, token);
  985. break;
  986. }
  987. case InsertionMode.TEXT: {
  988. endTagInText(this, token);
  989. break;
  990. }
  991. case InsertionMode.IN_TABLE: {
  992. endTagInTable(this, token);
  993. break;
  994. }
  995. case InsertionMode.IN_TABLE_TEXT: {
  996. tokenInTableText(this, token);
  997. break;
  998. }
  999. case InsertionMode.IN_CAPTION: {
  1000. endTagInCaption(this, token);
  1001. break;
  1002. }
  1003. case InsertionMode.IN_COLUMN_GROUP: {
  1004. endTagInColumnGroup(this, token);
  1005. break;
  1006. }
  1007. case InsertionMode.IN_TABLE_BODY: {
  1008. endTagInTableBody(this, token);
  1009. break;
  1010. }
  1011. case InsertionMode.IN_ROW: {
  1012. endTagInRow(this, token);
  1013. break;
  1014. }
  1015. case InsertionMode.IN_CELL: {
  1016. endTagInCell(this, token);
  1017. break;
  1018. }
  1019. case InsertionMode.IN_SELECT: {
  1020. endTagInSelect(this, token);
  1021. break;
  1022. }
  1023. case InsertionMode.IN_SELECT_IN_TABLE: {
  1024. endTagInSelectInTable(this, token);
  1025. break;
  1026. }
  1027. case InsertionMode.IN_TEMPLATE: {
  1028. endTagInTemplate(this, token);
  1029. break;
  1030. }
  1031. case InsertionMode.AFTER_BODY: {
  1032. endTagAfterBody(this, token);
  1033. break;
  1034. }
  1035. case InsertionMode.IN_FRAMESET: {
  1036. endTagInFrameset(this, token);
  1037. break;
  1038. }
  1039. case InsertionMode.AFTER_FRAMESET: {
  1040. endTagAfterFrameset(this, token);
  1041. break;
  1042. }
  1043. case InsertionMode.AFTER_AFTER_BODY: {
  1044. tokenAfterAfterBody(this, token);
  1045. break;
  1046. }
  1047. default:
  1048. // Do nothing
  1049. }
  1050. }
  1051. /** @internal */
  1052. onEof(token) {
  1053. switch (this.insertionMode) {
  1054. case InsertionMode.INITIAL: {
  1055. tokenInInitialMode(this, token);
  1056. break;
  1057. }
  1058. case InsertionMode.BEFORE_HTML: {
  1059. tokenBeforeHtml(this, token);
  1060. break;
  1061. }
  1062. case InsertionMode.BEFORE_HEAD: {
  1063. tokenBeforeHead(this, token);
  1064. break;
  1065. }
  1066. case InsertionMode.IN_HEAD: {
  1067. tokenInHead(this, token);
  1068. break;
  1069. }
  1070. case InsertionMode.IN_HEAD_NO_SCRIPT: {
  1071. tokenInHeadNoScript(this, token);
  1072. break;
  1073. }
  1074. case InsertionMode.AFTER_HEAD: {
  1075. tokenAfterHead(this, token);
  1076. break;
  1077. }
  1078. case InsertionMode.IN_BODY:
  1079. case InsertionMode.IN_TABLE:
  1080. case InsertionMode.IN_CAPTION:
  1081. case InsertionMode.IN_COLUMN_GROUP:
  1082. case InsertionMode.IN_TABLE_BODY:
  1083. case InsertionMode.IN_ROW:
  1084. case InsertionMode.IN_CELL:
  1085. case InsertionMode.IN_SELECT:
  1086. case InsertionMode.IN_SELECT_IN_TABLE: {
  1087. eofInBody(this, token);
  1088. break;
  1089. }
  1090. case InsertionMode.TEXT: {
  1091. eofInText(this, token);
  1092. break;
  1093. }
  1094. case InsertionMode.IN_TABLE_TEXT: {
  1095. tokenInTableText(this, token);
  1096. break;
  1097. }
  1098. case InsertionMode.IN_TEMPLATE: {
  1099. eofInTemplate(this, token);
  1100. break;
  1101. }
  1102. case InsertionMode.AFTER_BODY:
  1103. case InsertionMode.IN_FRAMESET:
  1104. case InsertionMode.AFTER_FRAMESET:
  1105. case InsertionMode.AFTER_AFTER_BODY:
  1106. case InsertionMode.AFTER_AFTER_FRAMESET: {
  1107. stopParsing(this, token);
  1108. break;
  1109. }
  1110. default:
  1111. // Do nothing
  1112. }
  1113. }
  1114. /** @internal */
  1115. onWhitespaceCharacter(token) {
  1116. if (this.skipNextNewLine) {
  1117. this.skipNextNewLine = false;
  1118. if (token.chars.charCodeAt(0) === unicode.CODE_POINTS.LINE_FEED) {
  1119. if (token.chars.length === 1) {
  1120. return;
  1121. }
  1122. token.chars = token.chars.substr(1);
  1123. }
  1124. }
  1125. if (this.tokenizer.inForeignNode) {
  1126. this._insertCharacters(token);
  1127. return;
  1128. }
  1129. switch (this.insertionMode) {
  1130. case InsertionMode.IN_HEAD:
  1131. case InsertionMode.IN_HEAD_NO_SCRIPT:
  1132. case InsertionMode.AFTER_HEAD:
  1133. case InsertionMode.TEXT:
  1134. case InsertionMode.IN_COLUMN_GROUP:
  1135. case InsertionMode.IN_SELECT:
  1136. case InsertionMode.IN_SELECT_IN_TABLE:
  1137. case InsertionMode.IN_FRAMESET:
  1138. case InsertionMode.AFTER_FRAMESET: {
  1139. this._insertCharacters(token);
  1140. break;
  1141. }
  1142. case InsertionMode.IN_BODY:
  1143. case InsertionMode.IN_CAPTION:
  1144. case InsertionMode.IN_CELL:
  1145. case InsertionMode.IN_TEMPLATE:
  1146. case InsertionMode.AFTER_BODY:
  1147. case InsertionMode.AFTER_AFTER_BODY:
  1148. case InsertionMode.AFTER_AFTER_FRAMESET: {
  1149. whitespaceCharacterInBody(this, token);
  1150. break;
  1151. }
  1152. case InsertionMode.IN_TABLE:
  1153. case InsertionMode.IN_TABLE_BODY:
  1154. case InsertionMode.IN_ROW: {
  1155. characterInTable(this, token);
  1156. break;
  1157. }
  1158. case InsertionMode.IN_TABLE_TEXT: {
  1159. whitespaceCharacterInTableText(this, token);
  1160. break;
  1161. }
  1162. default:
  1163. // Do nothing
  1164. }
  1165. }
  1166. }
  1167. exports.Parser = Parser;
  1168. //Adoption agency algorithm
  1169. //(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency)
  1170. //------------------------------------------------------------------
  1171. //Steps 5-8 of the algorithm
  1172. function aaObtainFormattingElementEntry(p, token) {
  1173. let formattingElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(token.tagName);
  1174. if (formattingElementEntry) {
  1175. if (!p.openElements.contains(formattingElementEntry.element)) {
  1176. p.activeFormattingElements.removeEntry(formattingElementEntry);
  1177. formattingElementEntry = null;
  1178. }
  1179. else if (!p.openElements.hasInScope(token.tagID)) {
  1180. formattingElementEntry = null;
  1181. }
  1182. }
  1183. else {
  1184. genericEndTagInBody(p, token);
  1185. }
  1186. return formattingElementEntry;
  1187. }
  1188. //Steps 9 and 10 of the algorithm
  1189. function aaObtainFurthestBlock(p, formattingElementEntry) {
  1190. let furthestBlock = null;
  1191. let idx = p.openElements.stackTop;
  1192. for (; idx >= 0; idx--) {
  1193. const element = p.openElements.items[idx];
  1194. if (element === formattingElementEntry.element) {
  1195. break;
  1196. }
  1197. if (p._isSpecialElement(element, p.openElements.tagIDs[idx])) {
  1198. furthestBlock = element;
  1199. }
  1200. }
  1201. if (!furthestBlock) {
  1202. p.openElements.shortenToLength(idx < 0 ? 0 : idx);
  1203. p.activeFormattingElements.removeEntry(formattingElementEntry);
  1204. }
  1205. return furthestBlock;
  1206. }
  1207. //Step 13 of the algorithm
  1208. function aaInnerLoop(p, furthestBlock, formattingElement) {
  1209. let lastElement = furthestBlock;
  1210. let nextElement = p.openElements.getCommonAncestor(furthestBlock);
  1211. for (let i = 0, element = nextElement; element !== formattingElement; i++, element = nextElement) {
  1212. //NOTE: store the next element for the next loop iteration (it may be deleted from the stack by step 9.5)
  1213. nextElement = p.openElements.getCommonAncestor(element);
  1214. const elementEntry = p.activeFormattingElements.getElementEntry(element);
  1215. const counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER;
  1216. const shouldRemoveFromOpenElements = !elementEntry || counterOverflow;
  1217. if (shouldRemoveFromOpenElements) {
  1218. if (counterOverflow) {
  1219. p.activeFormattingElements.removeEntry(elementEntry);
  1220. }
  1221. p.openElements.remove(element);
  1222. }
  1223. else {
  1224. element = aaRecreateElementFromEntry(p, elementEntry);
  1225. if (lastElement === furthestBlock) {
  1226. p.activeFormattingElements.bookmark = elementEntry;
  1227. }
  1228. p.treeAdapter.detachNode(lastElement);
  1229. p.treeAdapter.appendChild(element, lastElement);
  1230. lastElement = element;
  1231. }
  1232. }
  1233. return lastElement;
  1234. }
  1235. //Step 13.7 of the algorithm
  1236. function aaRecreateElementFromEntry(p, elementEntry) {
  1237. const ns = p.treeAdapter.getNamespaceURI(elementEntry.element);
  1238. const newElement = p.treeAdapter.createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs);
  1239. p.openElements.replace(elementEntry.element, newElement);
  1240. elementEntry.element = newElement;
  1241. return newElement;
  1242. }
  1243. //Step 14 of the algorithm
  1244. function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) {
  1245. const tn = p.treeAdapter.getTagName(commonAncestor);
  1246. const tid = (0, html_js_1.getTagID)(tn);
  1247. if (p._isElementCausesFosterParenting(tid)) {
  1248. p._fosterParentElement(lastElement);
  1249. }
  1250. else {
  1251. const ns = p.treeAdapter.getNamespaceURI(commonAncestor);
  1252. if (tid === html_js_1.TAG_ID.TEMPLATE && ns === html_js_1.NS.HTML) {
  1253. commonAncestor = p.treeAdapter.getTemplateContent(commonAncestor);
  1254. }
  1255. p.treeAdapter.appendChild(commonAncestor, lastElement);
  1256. }
  1257. }
  1258. //Steps 15-19 of the algorithm
  1259. function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) {
  1260. const ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element);
  1261. const { token } = formattingElementEntry;
  1262. const newElement = p.treeAdapter.createElement(token.tagName, ns, token.attrs);
  1263. p._adoptNodes(furthestBlock, newElement);
  1264. p.treeAdapter.appendChild(furthestBlock, newElement);
  1265. p.activeFormattingElements.insertElementAfterBookmark(newElement, token);
  1266. p.activeFormattingElements.removeEntry(formattingElementEntry);
  1267. p.openElements.remove(formattingElementEntry.element);
  1268. p.openElements.insertAfter(furthestBlock, newElement, token.tagID);
  1269. }
  1270. //Algorithm entry point
  1271. function callAdoptionAgency(p, token) {
  1272. for (let i = 0; i < AA_OUTER_LOOP_ITER; i++) {
  1273. const formattingElementEntry = aaObtainFormattingElementEntry(p, token);
  1274. if (!formattingElementEntry) {
  1275. break;
  1276. }
  1277. const furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry);
  1278. if (!furthestBlock) {
  1279. break;
  1280. }
  1281. p.activeFormattingElements.bookmark = formattingElementEntry;
  1282. const lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element);
  1283. const commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element);
  1284. p.treeAdapter.detachNode(lastElement);
  1285. if (commonAncestor)
  1286. aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement);
  1287. aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry);
  1288. }
  1289. }
  1290. //Generic token handlers
  1291. //------------------------------------------------------------------
  1292. function appendComment(p, token) {
  1293. p._appendCommentNode(token, p.openElements.currentTmplContentOrNode);
  1294. }
  1295. function appendCommentToRootHtmlElement(p, token) {
  1296. p._appendCommentNode(token, p.openElements.items[0]);
  1297. }
  1298. function appendCommentToDocument(p, token) {
  1299. p._appendCommentNode(token, p.document);
  1300. }
  1301. function stopParsing(p, token) {
  1302. p.stopped = true;
  1303. // NOTE: Set end locations for elements that remain on the open element stack.
  1304. if (token.location) {
  1305. // NOTE: If we are not in a fragment, `html` and `body` will stay on the stack.
  1306. // This is a problem, as we might overwrite their end position here.
  1307. const target = p.fragmentContext ? 0 : 2;
  1308. for (let i = p.openElements.stackTop; i >= target; i--) {
  1309. p._setEndLocation(p.openElements.items[i], token);
  1310. }
  1311. // Handle `html` and `body`
  1312. if (!p.fragmentContext && p.openElements.stackTop >= 0) {
  1313. const htmlElement = p.openElements.items[0];
  1314. const htmlLocation = p.treeAdapter.getNodeSourceCodeLocation(htmlElement);
  1315. if (htmlLocation && !htmlLocation.endTag) {
  1316. p._setEndLocation(htmlElement, token);
  1317. if (p.openElements.stackTop >= 1) {
  1318. const bodyElement = p.openElements.items[1];
  1319. const bodyLocation = p.treeAdapter.getNodeSourceCodeLocation(bodyElement);
  1320. if (bodyLocation && !bodyLocation.endTag) {
  1321. p._setEndLocation(bodyElement, token);
  1322. }
  1323. }
  1324. }
  1325. }
  1326. }
  1327. }
  1328. // The "initial" insertion mode
  1329. //------------------------------------------------------------------
  1330. function doctypeInInitialMode(p, token) {
  1331. p._setDocumentType(token);
  1332. const mode = token.forceQuirks ? html_js_1.DOCUMENT_MODE.QUIRKS : doctype.getDocumentMode(token);
  1333. if (!doctype.isConforming(token)) {
  1334. p._err(token, error_codes_js_1.ERR.nonConformingDoctype);
  1335. }
  1336. p.treeAdapter.setDocumentMode(p.document, mode);
  1337. p.insertionMode = InsertionMode.BEFORE_HTML;
  1338. }
  1339. function tokenInInitialMode(p, token) {
  1340. p._err(token, error_codes_js_1.ERR.missingDoctype, true);
  1341. p.treeAdapter.setDocumentMode(p.document, html_js_1.DOCUMENT_MODE.QUIRKS);
  1342. p.insertionMode = InsertionMode.BEFORE_HTML;
  1343. p._processToken(token);
  1344. }
  1345. // The "before html" insertion mode
  1346. //------------------------------------------------------------------
  1347. function startTagBeforeHtml(p, token) {
  1348. if (token.tagID === html_js_1.TAG_ID.HTML) {
  1349. p._insertElement(token, html_js_1.NS.HTML);
  1350. p.insertionMode = InsertionMode.BEFORE_HEAD;
  1351. }
  1352. else {
  1353. tokenBeforeHtml(p, token);
  1354. }
  1355. }
  1356. function endTagBeforeHtml(p, token) {
  1357. const tn = token.tagID;
  1358. if (tn === html_js_1.TAG_ID.HTML || tn === html_js_1.TAG_ID.HEAD || tn === html_js_1.TAG_ID.BODY || tn === html_js_1.TAG_ID.BR) {
  1359. tokenBeforeHtml(p, token);
  1360. }
  1361. }
  1362. function tokenBeforeHtml(p, token) {
  1363. p._insertFakeRootElement();
  1364. p.insertionMode = InsertionMode.BEFORE_HEAD;
  1365. p._processToken(token);
  1366. }
  1367. // The "before head" insertion mode
  1368. //------------------------------------------------------------------
  1369. function startTagBeforeHead(p, token) {
  1370. switch (token.tagID) {
  1371. case html_js_1.TAG_ID.HTML: {
  1372. startTagInBody(p, token);
  1373. break;
  1374. }
  1375. case html_js_1.TAG_ID.HEAD: {
  1376. p._insertElement(token, html_js_1.NS.HTML);
  1377. p.headElement = p.openElements.current;
  1378. p.insertionMode = InsertionMode.IN_HEAD;
  1379. break;
  1380. }
  1381. default: {
  1382. tokenBeforeHead(p, token);
  1383. }
  1384. }
  1385. }
  1386. function endTagBeforeHead(p, token) {
  1387. const tn = token.tagID;
  1388. if (tn === html_js_1.TAG_ID.HEAD || tn === html_js_1.TAG_ID.BODY || tn === html_js_1.TAG_ID.HTML || tn === html_js_1.TAG_ID.BR) {
  1389. tokenBeforeHead(p, token);
  1390. }
  1391. else {
  1392. p._err(token, error_codes_js_1.ERR.endTagWithoutMatchingOpenElement);
  1393. }
  1394. }
  1395. function tokenBeforeHead(p, token) {
  1396. p._insertFakeElement(html_js_1.TAG_NAMES.HEAD, html_js_1.TAG_ID.HEAD);
  1397. p.headElement = p.openElements.current;
  1398. p.insertionMode = InsertionMode.IN_HEAD;
  1399. p._processToken(token);
  1400. }
  1401. // The "in head" insertion mode
  1402. //------------------------------------------------------------------
  1403. function startTagInHead(p, token) {
  1404. switch (token.tagID) {
  1405. case html_js_1.TAG_ID.HTML: {
  1406. startTagInBody(p, token);
  1407. break;
  1408. }
  1409. case html_js_1.TAG_ID.BASE:
  1410. case html_js_1.TAG_ID.BASEFONT:
  1411. case html_js_1.TAG_ID.BGSOUND:
  1412. case html_js_1.TAG_ID.LINK:
  1413. case html_js_1.TAG_ID.META: {
  1414. p._appendElement(token, html_js_1.NS.HTML);
  1415. token.ackSelfClosing = true;
  1416. break;
  1417. }
  1418. case html_js_1.TAG_ID.TITLE: {
  1419. p._switchToTextParsing(token, index_js_1.TokenizerMode.RCDATA);
  1420. break;
  1421. }
  1422. case html_js_1.TAG_ID.NOSCRIPT: {
  1423. if (p.options.scriptingEnabled) {
  1424. p._switchToTextParsing(token, index_js_1.TokenizerMode.RAWTEXT);
  1425. }
  1426. else {
  1427. p._insertElement(token, html_js_1.NS.HTML);
  1428. p.insertionMode = InsertionMode.IN_HEAD_NO_SCRIPT;
  1429. }
  1430. break;
  1431. }
  1432. case html_js_1.TAG_ID.NOFRAMES:
  1433. case html_js_1.TAG_ID.STYLE: {
  1434. p._switchToTextParsing(token, index_js_1.TokenizerMode.RAWTEXT);
  1435. break;
  1436. }
  1437. case html_js_1.TAG_ID.SCRIPT: {
  1438. p._switchToTextParsing(token, index_js_1.TokenizerMode.SCRIPT_DATA);
  1439. break;
  1440. }
  1441. case html_js_1.TAG_ID.TEMPLATE: {
  1442. p._insertTemplate(token);
  1443. p.activeFormattingElements.insertMarker();
  1444. p.framesetOk = false;
  1445. p.insertionMode = InsertionMode.IN_TEMPLATE;
  1446. p.tmplInsertionModeStack.unshift(InsertionMode.IN_TEMPLATE);
  1447. break;
  1448. }
  1449. case html_js_1.TAG_ID.HEAD: {
  1450. p._err(token, error_codes_js_1.ERR.misplacedStartTagForHeadElement);
  1451. break;
  1452. }
  1453. default: {
  1454. tokenInHead(p, token);
  1455. }
  1456. }
  1457. }
  1458. function endTagInHead(p, token) {
  1459. switch (token.tagID) {
  1460. case html_js_1.TAG_ID.HEAD: {
  1461. p.openElements.pop();
  1462. p.insertionMode = InsertionMode.AFTER_HEAD;
  1463. break;
  1464. }
  1465. case html_js_1.TAG_ID.BODY:
  1466. case html_js_1.TAG_ID.BR:
  1467. case html_js_1.TAG_ID.HTML: {
  1468. tokenInHead(p, token);
  1469. break;
  1470. }
  1471. case html_js_1.TAG_ID.TEMPLATE: {
  1472. templateEndTagInHead(p, token);
  1473. break;
  1474. }
  1475. default: {
  1476. p._err(token, error_codes_js_1.ERR.endTagWithoutMatchingOpenElement);
  1477. }
  1478. }
  1479. }
  1480. function templateEndTagInHead(p, token) {
  1481. if (p.openElements.tmplCount > 0) {
  1482. p.openElements.generateImpliedEndTagsThoroughly();
  1483. if (p.openElements.currentTagId !== html_js_1.TAG_ID.TEMPLATE) {
  1484. p._err(token, error_codes_js_1.ERR.closingOfElementWithOpenChildElements);
  1485. }
  1486. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.TEMPLATE);
  1487. p.activeFormattingElements.clearToLastMarker();
  1488. p.tmplInsertionModeStack.shift();
  1489. p._resetInsertionMode();
  1490. }
  1491. else {
  1492. p._err(token, error_codes_js_1.ERR.endTagWithoutMatchingOpenElement);
  1493. }
  1494. }
  1495. function tokenInHead(p, token) {
  1496. p.openElements.pop();
  1497. p.insertionMode = InsertionMode.AFTER_HEAD;
  1498. p._processToken(token);
  1499. }
  1500. // The "in head no script" insertion mode
  1501. //------------------------------------------------------------------
  1502. function startTagInHeadNoScript(p, token) {
  1503. switch (token.tagID) {
  1504. case html_js_1.TAG_ID.HTML: {
  1505. startTagInBody(p, token);
  1506. break;
  1507. }
  1508. case html_js_1.TAG_ID.BASEFONT:
  1509. case html_js_1.TAG_ID.BGSOUND:
  1510. case html_js_1.TAG_ID.HEAD:
  1511. case html_js_1.TAG_ID.LINK:
  1512. case html_js_1.TAG_ID.META:
  1513. case html_js_1.TAG_ID.NOFRAMES:
  1514. case html_js_1.TAG_ID.STYLE: {
  1515. startTagInHead(p, token);
  1516. break;
  1517. }
  1518. case html_js_1.TAG_ID.NOSCRIPT: {
  1519. p._err(token, error_codes_js_1.ERR.nestedNoscriptInHead);
  1520. break;
  1521. }
  1522. default: {
  1523. tokenInHeadNoScript(p, token);
  1524. }
  1525. }
  1526. }
  1527. function endTagInHeadNoScript(p, token) {
  1528. switch (token.tagID) {
  1529. case html_js_1.TAG_ID.NOSCRIPT: {
  1530. p.openElements.pop();
  1531. p.insertionMode = InsertionMode.IN_HEAD;
  1532. break;
  1533. }
  1534. case html_js_1.TAG_ID.BR: {
  1535. tokenInHeadNoScript(p, token);
  1536. break;
  1537. }
  1538. default: {
  1539. p._err(token, error_codes_js_1.ERR.endTagWithoutMatchingOpenElement);
  1540. }
  1541. }
  1542. }
  1543. function tokenInHeadNoScript(p, token) {
  1544. const errCode = token.type === token_js_1.TokenType.EOF ? error_codes_js_1.ERR.openElementsLeftAfterEof : error_codes_js_1.ERR.disallowedContentInNoscriptInHead;
  1545. p._err(token, errCode);
  1546. p.openElements.pop();
  1547. p.insertionMode = InsertionMode.IN_HEAD;
  1548. p._processToken(token);
  1549. }
  1550. // The "after head" insertion mode
  1551. //------------------------------------------------------------------
  1552. function startTagAfterHead(p, token) {
  1553. switch (token.tagID) {
  1554. case html_js_1.TAG_ID.HTML: {
  1555. startTagInBody(p, token);
  1556. break;
  1557. }
  1558. case html_js_1.TAG_ID.BODY: {
  1559. p._insertElement(token, html_js_1.NS.HTML);
  1560. p.framesetOk = false;
  1561. p.insertionMode = InsertionMode.IN_BODY;
  1562. break;
  1563. }
  1564. case html_js_1.TAG_ID.FRAMESET: {
  1565. p._insertElement(token, html_js_1.NS.HTML);
  1566. p.insertionMode = InsertionMode.IN_FRAMESET;
  1567. break;
  1568. }
  1569. case html_js_1.TAG_ID.BASE:
  1570. case html_js_1.TAG_ID.BASEFONT:
  1571. case html_js_1.TAG_ID.BGSOUND:
  1572. case html_js_1.TAG_ID.LINK:
  1573. case html_js_1.TAG_ID.META:
  1574. case html_js_1.TAG_ID.NOFRAMES:
  1575. case html_js_1.TAG_ID.SCRIPT:
  1576. case html_js_1.TAG_ID.STYLE:
  1577. case html_js_1.TAG_ID.TEMPLATE:
  1578. case html_js_1.TAG_ID.TITLE: {
  1579. p._err(token, error_codes_js_1.ERR.abandonedHeadElementChild);
  1580. p.openElements.push(p.headElement, html_js_1.TAG_ID.HEAD);
  1581. startTagInHead(p, token);
  1582. p.openElements.remove(p.headElement);
  1583. break;
  1584. }
  1585. case html_js_1.TAG_ID.HEAD: {
  1586. p._err(token, error_codes_js_1.ERR.misplacedStartTagForHeadElement);
  1587. break;
  1588. }
  1589. default: {
  1590. tokenAfterHead(p, token);
  1591. }
  1592. }
  1593. }
  1594. function endTagAfterHead(p, token) {
  1595. switch (token.tagID) {
  1596. case html_js_1.TAG_ID.BODY:
  1597. case html_js_1.TAG_ID.HTML:
  1598. case html_js_1.TAG_ID.BR: {
  1599. tokenAfterHead(p, token);
  1600. break;
  1601. }
  1602. case html_js_1.TAG_ID.TEMPLATE: {
  1603. templateEndTagInHead(p, token);
  1604. break;
  1605. }
  1606. default: {
  1607. p._err(token, error_codes_js_1.ERR.endTagWithoutMatchingOpenElement);
  1608. }
  1609. }
  1610. }
  1611. function tokenAfterHead(p, token) {
  1612. p._insertFakeElement(html_js_1.TAG_NAMES.BODY, html_js_1.TAG_ID.BODY);
  1613. p.insertionMode = InsertionMode.IN_BODY;
  1614. modeInBody(p, token);
  1615. }
  1616. // The "in body" insertion mode
  1617. //------------------------------------------------------------------
  1618. function modeInBody(p, token) {
  1619. switch (token.type) {
  1620. case token_js_1.TokenType.CHARACTER: {
  1621. characterInBody(p, token);
  1622. break;
  1623. }
  1624. case token_js_1.TokenType.WHITESPACE_CHARACTER: {
  1625. whitespaceCharacterInBody(p, token);
  1626. break;
  1627. }
  1628. case token_js_1.TokenType.COMMENT: {
  1629. appendComment(p, token);
  1630. break;
  1631. }
  1632. case token_js_1.TokenType.START_TAG: {
  1633. startTagInBody(p, token);
  1634. break;
  1635. }
  1636. case token_js_1.TokenType.END_TAG: {
  1637. endTagInBody(p, token);
  1638. break;
  1639. }
  1640. case token_js_1.TokenType.EOF: {
  1641. eofInBody(p, token);
  1642. break;
  1643. }
  1644. default:
  1645. // Do nothing
  1646. }
  1647. }
  1648. function whitespaceCharacterInBody(p, token) {
  1649. p._reconstructActiveFormattingElements();
  1650. p._insertCharacters(token);
  1651. }
  1652. function characterInBody(p, token) {
  1653. p._reconstructActiveFormattingElements();
  1654. p._insertCharacters(token);
  1655. p.framesetOk = false;
  1656. }
  1657. function htmlStartTagInBody(p, token) {
  1658. if (p.openElements.tmplCount === 0) {
  1659. p.treeAdapter.adoptAttributes(p.openElements.items[0], token.attrs);
  1660. }
  1661. }
  1662. function bodyStartTagInBody(p, token) {
  1663. const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
  1664. if (bodyElement && p.openElements.tmplCount === 0) {
  1665. p.framesetOk = false;
  1666. p.treeAdapter.adoptAttributes(bodyElement, token.attrs);
  1667. }
  1668. }
  1669. function framesetStartTagInBody(p, token) {
  1670. const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
  1671. if (p.framesetOk && bodyElement) {
  1672. p.treeAdapter.detachNode(bodyElement);
  1673. p.openElements.popAllUpToHtmlElement();
  1674. p._insertElement(token, html_js_1.NS.HTML);
  1675. p.insertionMode = InsertionMode.IN_FRAMESET;
  1676. }
  1677. }
  1678. function addressStartTagInBody(p, token) {
  1679. if (p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1680. p._closePElement();
  1681. }
  1682. p._insertElement(token, html_js_1.NS.HTML);
  1683. }
  1684. function numberedHeaderStartTagInBody(p, token) {
  1685. if (p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1686. p._closePElement();
  1687. }
  1688. if (html_js_1.NUMBERED_HEADERS.has(p.openElements.currentTagId)) {
  1689. p.openElements.pop();
  1690. }
  1691. p._insertElement(token, html_js_1.NS.HTML);
  1692. }
  1693. function preStartTagInBody(p, token) {
  1694. if (p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1695. p._closePElement();
  1696. }
  1697. p._insertElement(token, html_js_1.NS.HTML);
  1698. //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
  1699. //on to the next one. (Newlines at the start of pre blocks are ignored as an authoring convenience.)
  1700. p.skipNextNewLine = true;
  1701. p.framesetOk = false;
  1702. }
  1703. function formStartTagInBody(p, token) {
  1704. const inTemplate = p.openElements.tmplCount > 0;
  1705. if (!p.formElement || inTemplate) {
  1706. if (p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1707. p._closePElement();
  1708. }
  1709. p._insertElement(token, html_js_1.NS.HTML);
  1710. if (!inTemplate) {
  1711. p.formElement = p.openElements.current;
  1712. }
  1713. }
  1714. }
  1715. function listItemStartTagInBody(p, token) {
  1716. p.framesetOk = false;
  1717. const tn = token.tagID;
  1718. for (let i = p.openElements.stackTop; i >= 0; i--) {
  1719. const elementId = p.openElements.tagIDs[i];
  1720. if ((tn === html_js_1.TAG_ID.LI && elementId === html_js_1.TAG_ID.LI) ||
  1721. ((tn === html_js_1.TAG_ID.DD || tn === html_js_1.TAG_ID.DT) && (elementId === html_js_1.TAG_ID.DD || elementId === html_js_1.TAG_ID.DT))) {
  1722. p.openElements.generateImpliedEndTagsWithExclusion(elementId);
  1723. p.openElements.popUntilTagNamePopped(elementId);
  1724. break;
  1725. }
  1726. if (elementId !== html_js_1.TAG_ID.ADDRESS &&
  1727. elementId !== html_js_1.TAG_ID.DIV &&
  1728. elementId !== html_js_1.TAG_ID.P &&
  1729. p._isSpecialElement(p.openElements.items[i], elementId)) {
  1730. break;
  1731. }
  1732. }
  1733. if (p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1734. p._closePElement();
  1735. }
  1736. p._insertElement(token, html_js_1.NS.HTML);
  1737. }
  1738. function plaintextStartTagInBody(p, token) {
  1739. if (p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1740. p._closePElement();
  1741. }
  1742. p._insertElement(token, html_js_1.NS.HTML);
  1743. p.tokenizer.state = index_js_1.TokenizerMode.PLAINTEXT;
  1744. }
  1745. function buttonStartTagInBody(p, token) {
  1746. if (p.openElements.hasInScope(html_js_1.TAG_ID.BUTTON)) {
  1747. p.openElements.generateImpliedEndTags();
  1748. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.BUTTON);
  1749. }
  1750. p._reconstructActiveFormattingElements();
  1751. p._insertElement(token, html_js_1.NS.HTML);
  1752. p.framesetOk = false;
  1753. }
  1754. function aStartTagInBody(p, token) {
  1755. const activeElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(html_js_1.TAG_NAMES.A);
  1756. if (activeElementEntry) {
  1757. callAdoptionAgency(p, token);
  1758. p.openElements.remove(activeElementEntry.element);
  1759. p.activeFormattingElements.removeEntry(activeElementEntry);
  1760. }
  1761. p._reconstructActiveFormattingElements();
  1762. p._insertElement(token, html_js_1.NS.HTML);
  1763. p.activeFormattingElements.pushElement(p.openElements.current, token);
  1764. }
  1765. function bStartTagInBody(p, token) {
  1766. p._reconstructActiveFormattingElements();
  1767. p._insertElement(token, html_js_1.NS.HTML);
  1768. p.activeFormattingElements.pushElement(p.openElements.current, token);
  1769. }
  1770. function nobrStartTagInBody(p, token) {
  1771. p._reconstructActiveFormattingElements();
  1772. if (p.openElements.hasInScope(html_js_1.TAG_ID.NOBR)) {
  1773. callAdoptionAgency(p, token);
  1774. p._reconstructActiveFormattingElements();
  1775. }
  1776. p._insertElement(token, html_js_1.NS.HTML);
  1777. p.activeFormattingElements.pushElement(p.openElements.current, token);
  1778. }
  1779. function appletStartTagInBody(p, token) {
  1780. p._reconstructActiveFormattingElements();
  1781. p._insertElement(token, html_js_1.NS.HTML);
  1782. p.activeFormattingElements.insertMarker();
  1783. p.framesetOk = false;
  1784. }
  1785. function tableStartTagInBody(p, token) {
  1786. if (p.treeAdapter.getDocumentMode(p.document) !== html_js_1.DOCUMENT_MODE.QUIRKS && p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1787. p._closePElement();
  1788. }
  1789. p._insertElement(token, html_js_1.NS.HTML);
  1790. p.framesetOk = false;
  1791. p.insertionMode = InsertionMode.IN_TABLE;
  1792. }
  1793. function areaStartTagInBody(p, token) {
  1794. p._reconstructActiveFormattingElements();
  1795. p._appendElement(token, html_js_1.NS.HTML);
  1796. p.framesetOk = false;
  1797. token.ackSelfClosing = true;
  1798. }
  1799. function isHiddenInput(token) {
  1800. const inputType = (0, token_js_1.getTokenAttr)(token, html_js_1.ATTRS.TYPE);
  1801. return inputType != null && inputType.toLowerCase() === HIDDEN_INPUT_TYPE;
  1802. }
  1803. function inputStartTagInBody(p, token) {
  1804. p._reconstructActiveFormattingElements();
  1805. p._appendElement(token, html_js_1.NS.HTML);
  1806. if (!isHiddenInput(token)) {
  1807. p.framesetOk = false;
  1808. }
  1809. token.ackSelfClosing = true;
  1810. }
  1811. function paramStartTagInBody(p, token) {
  1812. p._appendElement(token, html_js_1.NS.HTML);
  1813. token.ackSelfClosing = true;
  1814. }
  1815. function hrStartTagInBody(p, token) {
  1816. if (p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1817. p._closePElement();
  1818. }
  1819. p._appendElement(token, html_js_1.NS.HTML);
  1820. p.framesetOk = false;
  1821. token.ackSelfClosing = true;
  1822. }
  1823. function imageStartTagInBody(p, token) {
  1824. token.tagName = html_js_1.TAG_NAMES.IMG;
  1825. token.tagID = html_js_1.TAG_ID.IMG;
  1826. areaStartTagInBody(p, token);
  1827. }
  1828. function textareaStartTagInBody(p, token) {
  1829. p._insertElement(token, html_js_1.NS.HTML);
  1830. //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
  1831. //on to the next one. (Newlines at the start of textarea elements are ignored as an authoring convenience.)
  1832. p.skipNextNewLine = true;
  1833. p.tokenizer.state = index_js_1.TokenizerMode.RCDATA;
  1834. p.originalInsertionMode = p.insertionMode;
  1835. p.framesetOk = false;
  1836. p.insertionMode = InsertionMode.TEXT;
  1837. }
  1838. function xmpStartTagInBody(p, token) {
  1839. if (p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  1840. p._closePElement();
  1841. }
  1842. p._reconstructActiveFormattingElements();
  1843. p.framesetOk = false;
  1844. p._switchToTextParsing(token, index_js_1.TokenizerMode.RAWTEXT);
  1845. }
  1846. function iframeStartTagInBody(p, token) {
  1847. p.framesetOk = false;
  1848. p._switchToTextParsing(token, index_js_1.TokenizerMode.RAWTEXT);
  1849. }
  1850. //NOTE: here we assume that we always act as a user agent with enabled plugins/frames, so we parse
  1851. //<noembed>/<noframes> as rawtext.
  1852. function rawTextStartTagInBody(p, token) {
  1853. p._switchToTextParsing(token, index_js_1.TokenizerMode.RAWTEXT);
  1854. }
  1855. function selectStartTagInBody(p, token) {
  1856. p._reconstructActiveFormattingElements();
  1857. p._insertElement(token, html_js_1.NS.HTML);
  1858. p.framesetOk = false;
  1859. p.insertionMode =
  1860. p.insertionMode === InsertionMode.IN_TABLE ||
  1861. p.insertionMode === InsertionMode.IN_CAPTION ||
  1862. p.insertionMode === InsertionMode.IN_TABLE_BODY ||
  1863. p.insertionMode === InsertionMode.IN_ROW ||
  1864. p.insertionMode === InsertionMode.IN_CELL
  1865. ? InsertionMode.IN_SELECT_IN_TABLE
  1866. : InsertionMode.IN_SELECT;
  1867. }
  1868. function optgroupStartTagInBody(p, token) {
  1869. if (p.openElements.currentTagId === html_js_1.TAG_ID.OPTION) {
  1870. p.openElements.pop();
  1871. }
  1872. p._reconstructActiveFormattingElements();
  1873. p._insertElement(token, html_js_1.NS.HTML);
  1874. }
  1875. function rbStartTagInBody(p, token) {
  1876. if (p.openElements.hasInScope(html_js_1.TAG_ID.RUBY)) {
  1877. p.openElements.generateImpliedEndTags();
  1878. }
  1879. p._insertElement(token, html_js_1.NS.HTML);
  1880. }
  1881. function rtStartTagInBody(p, token) {
  1882. if (p.openElements.hasInScope(html_js_1.TAG_ID.RUBY)) {
  1883. p.openElements.generateImpliedEndTagsWithExclusion(html_js_1.TAG_ID.RTC);
  1884. }
  1885. p._insertElement(token, html_js_1.NS.HTML);
  1886. }
  1887. function mathStartTagInBody(p, token) {
  1888. p._reconstructActiveFormattingElements();
  1889. foreignContent.adjustTokenMathMLAttrs(token);
  1890. foreignContent.adjustTokenXMLAttrs(token);
  1891. if (token.selfClosing) {
  1892. p._appendElement(token, html_js_1.NS.MATHML);
  1893. }
  1894. else {
  1895. p._insertElement(token, html_js_1.NS.MATHML);
  1896. }
  1897. token.ackSelfClosing = true;
  1898. }
  1899. function svgStartTagInBody(p, token) {
  1900. p._reconstructActiveFormattingElements();
  1901. foreignContent.adjustTokenSVGAttrs(token);
  1902. foreignContent.adjustTokenXMLAttrs(token);
  1903. if (token.selfClosing) {
  1904. p._appendElement(token, html_js_1.NS.SVG);
  1905. }
  1906. else {
  1907. p._insertElement(token, html_js_1.NS.SVG);
  1908. }
  1909. token.ackSelfClosing = true;
  1910. }
  1911. function genericStartTagInBody(p, token) {
  1912. p._reconstructActiveFormattingElements();
  1913. p._insertElement(token, html_js_1.NS.HTML);
  1914. }
  1915. function startTagInBody(p, token) {
  1916. switch (token.tagID) {
  1917. case html_js_1.TAG_ID.I:
  1918. case html_js_1.TAG_ID.S:
  1919. case html_js_1.TAG_ID.B:
  1920. case html_js_1.TAG_ID.U:
  1921. case html_js_1.TAG_ID.EM:
  1922. case html_js_1.TAG_ID.TT:
  1923. case html_js_1.TAG_ID.BIG:
  1924. case html_js_1.TAG_ID.CODE:
  1925. case html_js_1.TAG_ID.FONT:
  1926. case html_js_1.TAG_ID.SMALL:
  1927. case html_js_1.TAG_ID.STRIKE:
  1928. case html_js_1.TAG_ID.STRONG: {
  1929. bStartTagInBody(p, token);
  1930. break;
  1931. }
  1932. case html_js_1.TAG_ID.A: {
  1933. aStartTagInBody(p, token);
  1934. break;
  1935. }
  1936. case html_js_1.TAG_ID.H1:
  1937. case html_js_1.TAG_ID.H2:
  1938. case html_js_1.TAG_ID.H3:
  1939. case html_js_1.TAG_ID.H4:
  1940. case html_js_1.TAG_ID.H5:
  1941. case html_js_1.TAG_ID.H6: {
  1942. numberedHeaderStartTagInBody(p, token);
  1943. break;
  1944. }
  1945. case html_js_1.TAG_ID.P:
  1946. case html_js_1.TAG_ID.DL:
  1947. case html_js_1.TAG_ID.OL:
  1948. case html_js_1.TAG_ID.UL:
  1949. case html_js_1.TAG_ID.DIV:
  1950. case html_js_1.TAG_ID.DIR:
  1951. case html_js_1.TAG_ID.NAV:
  1952. case html_js_1.TAG_ID.MAIN:
  1953. case html_js_1.TAG_ID.MENU:
  1954. case html_js_1.TAG_ID.ASIDE:
  1955. case html_js_1.TAG_ID.CENTER:
  1956. case html_js_1.TAG_ID.FIGURE:
  1957. case html_js_1.TAG_ID.FOOTER:
  1958. case html_js_1.TAG_ID.HEADER:
  1959. case html_js_1.TAG_ID.HGROUP:
  1960. case html_js_1.TAG_ID.DIALOG:
  1961. case html_js_1.TAG_ID.DETAILS:
  1962. case html_js_1.TAG_ID.ADDRESS:
  1963. case html_js_1.TAG_ID.ARTICLE:
  1964. case html_js_1.TAG_ID.SEARCH:
  1965. case html_js_1.TAG_ID.SECTION:
  1966. case html_js_1.TAG_ID.SUMMARY:
  1967. case html_js_1.TAG_ID.FIELDSET:
  1968. case html_js_1.TAG_ID.BLOCKQUOTE:
  1969. case html_js_1.TAG_ID.FIGCAPTION: {
  1970. addressStartTagInBody(p, token);
  1971. break;
  1972. }
  1973. case html_js_1.TAG_ID.LI:
  1974. case html_js_1.TAG_ID.DD:
  1975. case html_js_1.TAG_ID.DT: {
  1976. listItemStartTagInBody(p, token);
  1977. break;
  1978. }
  1979. case html_js_1.TAG_ID.BR:
  1980. case html_js_1.TAG_ID.IMG:
  1981. case html_js_1.TAG_ID.WBR:
  1982. case html_js_1.TAG_ID.AREA:
  1983. case html_js_1.TAG_ID.EMBED:
  1984. case html_js_1.TAG_ID.KEYGEN: {
  1985. areaStartTagInBody(p, token);
  1986. break;
  1987. }
  1988. case html_js_1.TAG_ID.HR: {
  1989. hrStartTagInBody(p, token);
  1990. break;
  1991. }
  1992. case html_js_1.TAG_ID.RB:
  1993. case html_js_1.TAG_ID.RTC: {
  1994. rbStartTagInBody(p, token);
  1995. break;
  1996. }
  1997. case html_js_1.TAG_ID.RT:
  1998. case html_js_1.TAG_ID.RP: {
  1999. rtStartTagInBody(p, token);
  2000. break;
  2001. }
  2002. case html_js_1.TAG_ID.PRE:
  2003. case html_js_1.TAG_ID.LISTING: {
  2004. preStartTagInBody(p, token);
  2005. break;
  2006. }
  2007. case html_js_1.TAG_ID.XMP: {
  2008. xmpStartTagInBody(p, token);
  2009. break;
  2010. }
  2011. case html_js_1.TAG_ID.SVG: {
  2012. svgStartTagInBody(p, token);
  2013. break;
  2014. }
  2015. case html_js_1.TAG_ID.HTML: {
  2016. htmlStartTagInBody(p, token);
  2017. break;
  2018. }
  2019. case html_js_1.TAG_ID.BASE:
  2020. case html_js_1.TAG_ID.LINK:
  2021. case html_js_1.TAG_ID.META:
  2022. case html_js_1.TAG_ID.STYLE:
  2023. case html_js_1.TAG_ID.TITLE:
  2024. case html_js_1.TAG_ID.SCRIPT:
  2025. case html_js_1.TAG_ID.BGSOUND:
  2026. case html_js_1.TAG_ID.BASEFONT:
  2027. case html_js_1.TAG_ID.TEMPLATE: {
  2028. startTagInHead(p, token);
  2029. break;
  2030. }
  2031. case html_js_1.TAG_ID.BODY: {
  2032. bodyStartTagInBody(p, token);
  2033. break;
  2034. }
  2035. case html_js_1.TAG_ID.FORM: {
  2036. formStartTagInBody(p, token);
  2037. break;
  2038. }
  2039. case html_js_1.TAG_ID.NOBR: {
  2040. nobrStartTagInBody(p, token);
  2041. break;
  2042. }
  2043. case html_js_1.TAG_ID.MATH: {
  2044. mathStartTagInBody(p, token);
  2045. break;
  2046. }
  2047. case html_js_1.TAG_ID.TABLE: {
  2048. tableStartTagInBody(p, token);
  2049. break;
  2050. }
  2051. case html_js_1.TAG_ID.INPUT: {
  2052. inputStartTagInBody(p, token);
  2053. break;
  2054. }
  2055. case html_js_1.TAG_ID.PARAM:
  2056. case html_js_1.TAG_ID.TRACK:
  2057. case html_js_1.TAG_ID.SOURCE: {
  2058. paramStartTagInBody(p, token);
  2059. break;
  2060. }
  2061. case html_js_1.TAG_ID.IMAGE: {
  2062. imageStartTagInBody(p, token);
  2063. break;
  2064. }
  2065. case html_js_1.TAG_ID.BUTTON: {
  2066. buttonStartTagInBody(p, token);
  2067. break;
  2068. }
  2069. case html_js_1.TAG_ID.APPLET:
  2070. case html_js_1.TAG_ID.OBJECT:
  2071. case html_js_1.TAG_ID.MARQUEE: {
  2072. appletStartTagInBody(p, token);
  2073. break;
  2074. }
  2075. case html_js_1.TAG_ID.IFRAME: {
  2076. iframeStartTagInBody(p, token);
  2077. break;
  2078. }
  2079. case html_js_1.TAG_ID.SELECT: {
  2080. selectStartTagInBody(p, token);
  2081. break;
  2082. }
  2083. case html_js_1.TAG_ID.OPTION:
  2084. case html_js_1.TAG_ID.OPTGROUP: {
  2085. optgroupStartTagInBody(p, token);
  2086. break;
  2087. }
  2088. case html_js_1.TAG_ID.NOEMBED:
  2089. case html_js_1.TAG_ID.NOFRAMES: {
  2090. rawTextStartTagInBody(p, token);
  2091. break;
  2092. }
  2093. case html_js_1.TAG_ID.FRAMESET: {
  2094. framesetStartTagInBody(p, token);
  2095. break;
  2096. }
  2097. case html_js_1.TAG_ID.TEXTAREA: {
  2098. textareaStartTagInBody(p, token);
  2099. break;
  2100. }
  2101. case html_js_1.TAG_ID.NOSCRIPT: {
  2102. if (p.options.scriptingEnabled) {
  2103. rawTextStartTagInBody(p, token);
  2104. }
  2105. else {
  2106. genericStartTagInBody(p, token);
  2107. }
  2108. break;
  2109. }
  2110. case html_js_1.TAG_ID.PLAINTEXT: {
  2111. plaintextStartTagInBody(p, token);
  2112. break;
  2113. }
  2114. case html_js_1.TAG_ID.COL:
  2115. case html_js_1.TAG_ID.TH:
  2116. case html_js_1.TAG_ID.TD:
  2117. case html_js_1.TAG_ID.TR:
  2118. case html_js_1.TAG_ID.HEAD:
  2119. case html_js_1.TAG_ID.FRAME:
  2120. case html_js_1.TAG_ID.TBODY:
  2121. case html_js_1.TAG_ID.TFOOT:
  2122. case html_js_1.TAG_ID.THEAD:
  2123. case html_js_1.TAG_ID.CAPTION:
  2124. case html_js_1.TAG_ID.COLGROUP: {
  2125. // Ignore token
  2126. break;
  2127. }
  2128. default: {
  2129. genericStartTagInBody(p, token);
  2130. }
  2131. }
  2132. }
  2133. function bodyEndTagInBody(p, token) {
  2134. if (p.openElements.hasInScope(html_js_1.TAG_ID.BODY)) {
  2135. p.insertionMode = InsertionMode.AFTER_BODY;
  2136. //NOTE: <body> is never popped from the stack, so we need to updated
  2137. //the end location explicitly.
  2138. if (p.options.sourceCodeLocationInfo) {
  2139. const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
  2140. if (bodyElement) {
  2141. p._setEndLocation(bodyElement, token);
  2142. }
  2143. }
  2144. }
  2145. }
  2146. function htmlEndTagInBody(p, token) {
  2147. if (p.openElements.hasInScope(html_js_1.TAG_ID.BODY)) {
  2148. p.insertionMode = InsertionMode.AFTER_BODY;
  2149. endTagAfterBody(p, token);
  2150. }
  2151. }
  2152. function addressEndTagInBody(p, token) {
  2153. const tn = token.tagID;
  2154. if (p.openElements.hasInScope(tn)) {
  2155. p.openElements.generateImpliedEndTags();
  2156. p.openElements.popUntilTagNamePopped(tn);
  2157. }
  2158. }
  2159. function formEndTagInBody(p) {
  2160. const inTemplate = p.openElements.tmplCount > 0;
  2161. const { formElement } = p;
  2162. if (!inTemplate) {
  2163. p.formElement = null;
  2164. }
  2165. if ((formElement || inTemplate) && p.openElements.hasInScope(html_js_1.TAG_ID.FORM)) {
  2166. p.openElements.generateImpliedEndTags();
  2167. if (inTemplate) {
  2168. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.FORM);
  2169. }
  2170. else if (formElement) {
  2171. p.openElements.remove(formElement);
  2172. }
  2173. }
  2174. }
  2175. function pEndTagInBody(p) {
  2176. if (!p.openElements.hasInButtonScope(html_js_1.TAG_ID.P)) {
  2177. p._insertFakeElement(html_js_1.TAG_NAMES.P, html_js_1.TAG_ID.P);
  2178. }
  2179. p._closePElement();
  2180. }
  2181. function liEndTagInBody(p) {
  2182. if (p.openElements.hasInListItemScope(html_js_1.TAG_ID.LI)) {
  2183. p.openElements.generateImpliedEndTagsWithExclusion(html_js_1.TAG_ID.LI);
  2184. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.LI);
  2185. }
  2186. }
  2187. function ddEndTagInBody(p, token) {
  2188. const tn = token.tagID;
  2189. if (p.openElements.hasInScope(tn)) {
  2190. p.openElements.generateImpliedEndTagsWithExclusion(tn);
  2191. p.openElements.popUntilTagNamePopped(tn);
  2192. }
  2193. }
  2194. function numberedHeaderEndTagInBody(p) {
  2195. if (p.openElements.hasNumberedHeaderInScope()) {
  2196. p.openElements.generateImpliedEndTags();
  2197. p.openElements.popUntilNumberedHeaderPopped();
  2198. }
  2199. }
  2200. function appletEndTagInBody(p, token) {
  2201. const tn = token.tagID;
  2202. if (p.openElements.hasInScope(tn)) {
  2203. p.openElements.generateImpliedEndTags();
  2204. p.openElements.popUntilTagNamePopped(tn);
  2205. p.activeFormattingElements.clearToLastMarker();
  2206. }
  2207. }
  2208. function brEndTagInBody(p) {
  2209. p._reconstructActiveFormattingElements();
  2210. p._insertFakeElement(html_js_1.TAG_NAMES.BR, html_js_1.TAG_ID.BR);
  2211. p.openElements.pop();
  2212. p.framesetOk = false;
  2213. }
  2214. function genericEndTagInBody(p, token) {
  2215. const tn = token.tagName;
  2216. const tid = token.tagID;
  2217. for (let i = p.openElements.stackTop; i > 0; i--) {
  2218. const element = p.openElements.items[i];
  2219. const elementId = p.openElements.tagIDs[i];
  2220. // Compare the tag name here, as the tag might not be a known tag with an ID.
  2221. if (tid === elementId && (tid !== html_js_1.TAG_ID.UNKNOWN || p.treeAdapter.getTagName(element) === tn)) {
  2222. p.openElements.generateImpliedEndTagsWithExclusion(tid);
  2223. if (p.openElements.stackTop >= i)
  2224. p.openElements.shortenToLength(i);
  2225. break;
  2226. }
  2227. if (p._isSpecialElement(element, elementId)) {
  2228. break;
  2229. }
  2230. }
  2231. }
  2232. function endTagInBody(p, token) {
  2233. switch (token.tagID) {
  2234. case html_js_1.TAG_ID.A:
  2235. case html_js_1.TAG_ID.B:
  2236. case html_js_1.TAG_ID.I:
  2237. case html_js_1.TAG_ID.S:
  2238. case html_js_1.TAG_ID.U:
  2239. case html_js_1.TAG_ID.EM:
  2240. case html_js_1.TAG_ID.TT:
  2241. case html_js_1.TAG_ID.BIG:
  2242. case html_js_1.TAG_ID.CODE:
  2243. case html_js_1.TAG_ID.FONT:
  2244. case html_js_1.TAG_ID.NOBR:
  2245. case html_js_1.TAG_ID.SMALL:
  2246. case html_js_1.TAG_ID.STRIKE:
  2247. case html_js_1.TAG_ID.STRONG: {
  2248. callAdoptionAgency(p, token);
  2249. break;
  2250. }
  2251. case html_js_1.TAG_ID.P: {
  2252. pEndTagInBody(p);
  2253. break;
  2254. }
  2255. case html_js_1.TAG_ID.DL:
  2256. case html_js_1.TAG_ID.UL:
  2257. case html_js_1.TAG_ID.OL:
  2258. case html_js_1.TAG_ID.DIR:
  2259. case html_js_1.TAG_ID.DIV:
  2260. case html_js_1.TAG_ID.NAV:
  2261. case html_js_1.TAG_ID.PRE:
  2262. case html_js_1.TAG_ID.MAIN:
  2263. case html_js_1.TAG_ID.MENU:
  2264. case html_js_1.TAG_ID.ASIDE:
  2265. case html_js_1.TAG_ID.BUTTON:
  2266. case html_js_1.TAG_ID.CENTER:
  2267. case html_js_1.TAG_ID.FIGURE:
  2268. case html_js_1.TAG_ID.FOOTER:
  2269. case html_js_1.TAG_ID.HEADER:
  2270. case html_js_1.TAG_ID.HGROUP:
  2271. case html_js_1.TAG_ID.DIALOG:
  2272. case html_js_1.TAG_ID.ADDRESS:
  2273. case html_js_1.TAG_ID.ARTICLE:
  2274. case html_js_1.TAG_ID.DETAILS:
  2275. case html_js_1.TAG_ID.SEARCH:
  2276. case html_js_1.TAG_ID.SECTION:
  2277. case html_js_1.TAG_ID.SUMMARY:
  2278. case html_js_1.TAG_ID.LISTING:
  2279. case html_js_1.TAG_ID.FIELDSET:
  2280. case html_js_1.TAG_ID.BLOCKQUOTE:
  2281. case html_js_1.TAG_ID.FIGCAPTION: {
  2282. addressEndTagInBody(p, token);
  2283. break;
  2284. }
  2285. case html_js_1.TAG_ID.LI: {
  2286. liEndTagInBody(p);
  2287. break;
  2288. }
  2289. case html_js_1.TAG_ID.DD:
  2290. case html_js_1.TAG_ID.DT: {
  2291. ddEndTagInBody(p, token);
  2292. break;
  2293. }
  2294. case html_js_1.TAG_ID.H1:
  2295. case html_js_1.TAG_ID.H2:
  2296. case html_js_1.TAG_ID.H3:
  2297. case html_js_1.TAG_ID.H4:
  2298. case html_js_1.TAG_ID.H5:
  2299. case html_js_1.TAG_ID.H6: {
  2300. numberedHeaderEndTagInBody(p);
  2301. break;
  2302. }
  2303. case html_js_1.TAG_ID.BR: {
  2304. brEndTagInBody(p);
  2305. break;
  2306. }
  2307. case html_js_1.TAG_ID.BODY: {
  2308. bodyEndTagInBody(p, token);
  2309. break;
  2310. }
  2311. case html_js_1.TAG_ID.HTML: {
  2312. htmlEndTagInBody(p, token);
  2313. break;
  2314. }
  2315. case html_js_1.TAG_ID.FORM: {
  2316. formEndTagInBody(p);
  2317. break;
  2318. }
  2319. case html_js_1.TAG_ID.APPLET:
  2320. case html_js_1.TAG_ID.OBJECT:
  2321. case html_js_1.TAG_ID.MARQUEE: {
  2322. appletEndTagInBody(p, token);
  2323. break;
  2324. }
  2325. case html_js_1.TAG_ID.TEMPLATE: {
  2326. templateEndTagInHead(p, token);
  2327. break;
  2328. }
  2329. default: {
  2330. genericEndTagInBody(p, token);
  2331. }
  2332. }
  2333. }
  2334. function eofInBody(p, token) {
  2335. if (p.tmplInsertionModeStack.length > 0) {
  2336. eofInTemplate(p, token);
  2337. }
  2338. else {
  2339. stopParsing(p, token);
  2340. }
  2341. }
  2342. // The "text" insertion mode
  2343. //------------------------------------------------------------------
  2344. function endTagInText(p, token) {
  2345. var _a;
  2346. if (token.tagID === html_js_1.TAG_ID.SCRIPT) {
  2347. (_a = p.scriptHandler) === null || _a === void 0 ? void 0 : _a.call(p, p.openElements.current);
  2348. }
  2349. p.openElements.pop();
  2350. p.insertionMode = p.originalInsertionMode;
  2351. }
  2352. function eofInText(p, token) {
  2353. p._err(token, error_codes_js_1.ERR.eofInElementThatCanContainOnlyText);
  2354. p.openElements.pop();
  2355. p.insertionMode = p.originalInsertionMode;
  2356. p.onEof(token);
  2357. }
  2358. // The "in table" insertion mode
  2359. //------------------------------------------------------------------
  2360. function characterInTable(p, token) {
  2361. if (TABLE_STRUCTURE_TAGS.has(p.openElements.currentTagId)) {
  2362. p.pendingCharacterTokens.length = 0;
  2363. p.hasNonWhitespacePendingCharacterToken = false;
  2364. p.originalInsertionMode = p.insertionMode;
  2365. p.insertionMode = InsertionMode.IN_TABLE_TEXT;
  2366. switch (token.type) {
  2367. case token_js_1.TokenType.CHARACTER: {
  2368. characterInTableText(p, token);
  2369. break;
  2370. }
  2371. case token_js_1.TokenType.WHITESPACE_CHARACTER: {
  2372. whitespaceCharacterInTableText(p, token);
  2373. break;
  2374. }
  2375. // Ignore null
  2376. }
  2377. }
  2378. else {
  2379. tokenInTable(p, token);
  2380. }
  2381. }
  2382. function captionStartTagInTable(p, token) {
  2383. p.openElements.clearBackToTableContext();
  2384. p.activeFormattingElements.insertMarker();
  2385. p._insertElement(token, html_js_1.NS.HTML);
  2386. p.insertionMode = InsertionMode.IN_CAPTION;
  2387. }
  2388. function colgroupStartTagInTable(p, token) {
  2389. p.openElements.clearBackToTableContext();
  2390. p._insertElement(token, html_js_1.NS.HTML);
  2391. p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
  2392. }
  2393. function colStartTagInTable(p, token) {
  2394. p.openElements.clearBackToTableContext();
  2395. p._insertFakeElement(html_js_1.TAG_NAMES.COLGROUP, html_js_1.TAG_ID.COLGROUP);
  2396. p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
  2397. startTagInColumnGroup(p, token);
  2398. }
  2399. function tbodyStartTagInTable(p, token) {
  2400. p.openElements.clearBackToTableContext();
  2401. p._insertElement(token, html_js_1.NS.HTML);
  2402. p.insertionMode = InsertionMode.IN_TABLE_BODY;
  2403. }
  2404. function tdStartTagInTable(p, token) {
  2405. p.openElements.clearBackToTableContext();
  2406. p._insertFakeElement(html_js_1.TAG_NAMES.TBODY, html_js_1.TAG_ID.TBODY);
  2407. p.insertionMode = InsertionMode.IN_TABLE_BODY;
  2408. startTagInTableBody(p, token);
  2409. }
  2410. function tableStartTagInTable(p, token) {
  2411. if (p.openElements.hasInTableScope(html_js_1.TAG_ID.TABLE)) {
  2412. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.TABLE);
  2413. p._resetInsertionMode();
  2414. p._processStartTag(token);
  2415. }
  2416. }
  2417. function inputStartTagInTable(p, token) {
  2418. if (isHiddenInput(token)) {
  2419. p._appendElement(token, html_js_1.NS.HTML);
  2420. }
  2421. else {
  2422. tokenInTable(p, token);
  2423. }
  2424. token.ackSelfClosing = true;
  2425. }
  2426. function formStartTagInTable(p, token) {
  2427. if (!p.formElement && p.openElements.tmplCount === 0) {
  2428. p._insertElement(token, html_js_1.NS.HTML);
  2429. p.formElement = p.openElements.current;
  2430. p.openElements.pop();
  2431. }
  2432. }
  2433. function startTagInTable(p, token) {
  2434. switch (token.tagID) {
  2435. case html_js_1.TAG_ID.TD:
  2436. case html_js_1.TAG_ID.TH:
  2437. case html_js_1.TAG_ID.TR: {
  2438. tdStartTagInTable(p, token);
  2439. break;
  2440. }
  2441. case html_js_1.TAG_ID.STYLE:
  2442. case html_js_1.TAG_ID.SCRIPT:
  2443. case html_js_1.TAG_ID.TEMPLATE: {
  2444. startTagInHead(p, token);
  2445. break;
  2446. }
  2447. case html_js_1.TAG_ID.COL: {
  2448. colStartTagInTable(p, token);
  2449. break;
  2450. }
  2451. case html_js_1.TAG_ID.FORM: {
  2452. formStartTagInTable(p, token);
  2453. break;
  2454. }
  2455. case html_js_1.TAG_ID.TABLE: {
  2456. tableStartTagInTable(p, token);
  2457. break;
  2458. }
  2459. case html_js_1.TAG_ID.TBODY:
  2460. case html_js_1.TAG_ID.TFOOT:
  2461. case html_js_1.TAG_ID.THEAD: {
  2462. tbodyStartTagInTable(p, token);
  2463. break;
  2464. }
  2465. case html_js_1.TAG_ID.INPUT: {
  2466. inputStartTagInTable(p, token);
  2467. break;
  2468. }
  2469. case html_js_1.TAG_ID.CAPTION: {
  2470. captionStartTagInTable(p, token);
  2471. break;
  2472. }
  2473. case html_js_1.TAG_ID.COLGROUP: {
  2474. colgroupStartTagInTable(p, token);
  2475. break;
  2476. }
  2477. default: {
  2478. tokenInTable(p, token);
  2479. }
  2480. }
  2481. }
  2482. function endTagInTable(p, token) {
  2483. switch (token.tagID) {
  2484. case html_js_1.TAG_ID.TABLE: {
  2485. if (p.openElements.hasInTableScope(html_js_1.TAG_ID.TABLE)) {
  2486. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.TABLE);
  2487. p._resetInsertionMode();
  2488. }
  2489. break;
  2490. }
  2491. case html_js_1.TAG_ID.TEMPLATE: {
  2492. templateEndTagInHead(p, token);
  2493. break;
  2494. }
  2495. case html_js_1.TAG_ID.BODY:
  2496. case html_js_1.TAG_ID.CAPTION:
  2497. case html_js_1.TAG_ID.COL:
  2498. case html_js_1.TAG_ID.COLGROUP:
  2499. case html_js_1.TAG_ID.HTML:
  2500. case html_js_1.TAG_ID.TBODY:
  2501. case html_js_1.TAG_ID.TD:
  2502. case html_js_1.TAG_ID.TFOOT:
  2503. case html_js_1.TAG_ID.TH:
  2504. case html_js_1.TAG_ID.THEAD:
  2505. case html_js_1.TAG_ID.TR: {
  2506. // Ignore token
  2507. break;
  2508. }
  2509. default: {
  2510. tokenInTable(p, token);
  2511. }
  2512. }
  2513. }
  2514. function tokenInTable(p, token) {
  2515. const savedFosterParentingState = p.fosterParentingEnabled;
  2516. p.fosterParentingEnabled = true;
  2517. // Process token in `In Body` mode
  2518. modeInBody(p, token);
  2519. p.fosterParentingEnabled = savedFosterParentingState;
  2520. }
  2521. // The "in table text" insertion mode
  2522. //------------------------------------------------------------------
  2523. function whitespaceCharacterInTableText(p, token) {
  2524. p.pendingCharacterTokens.push(token);
  2525. }
  2526. function characterInTableText(p, token) {
  2527. p.pendingCharacterTokens.push(token);
  2528. p.hasNonWhitespacePendingCharacterToken = true;
  2529. }
  2530. function tokenInTableText(p, token) {
  2531. let i = 0;
  2532. if (p.hasNonWhitespacePendingCharacterToken) {
  2533. for (; i < p.pendingCharacterTokens.length; i++) {
  2534. tokenInTable(p, p.pendingCharacterTokens[i]);
  2535. }
  2536. }
  2537. else {
  2538. for (; i < p.pendingCharacterTokens.length; i++) {
  2539. p._insertCharacters(p.pendingCharacterTokens[i]);
  2540. }
  2541. }
  2542. p.insertionMode = p.originalInsertionMode;
  2543. p._processToken(token);
  2544. }
  2545. // The "in caption" insertion mode
  2546. //------------------------------------------------------------------
  2547. const TABLE_VOID_ELEMENTS = new Set([html_js_1.TAG_ID.CAPTION, html_js_1.TAG_ID.COL, html_js_1.TAG_ID.COLGROUP, html_js_1.TAG_ID.TBODY, html_js_1.TAG_ID.TD, html_js_1.TAG_ID.TFOOT, html_js_1.TAG_ID.TH, html_js_1.TAG_ID.THEAD, html_js_1.TAG_ID.TR]);
  2548. function startTagInCaption(p, token) {
  2549. const tn = token.tagID;
  2550. if (TABLE_VOID_ELEMENTS.has(tn)) {
  2551. if (p.openElements.hasInTableScope(html_js_1.TAG_ID.CAPTION)) {
  2552. p.openElements.generateImpliedEndTags();
  2553. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.CAPTION);
  2554. p.activeFormattingElements.clearToLastMarker();
  2555. p.insertionMode = InsertionMode.IN_TABLE;
  2556. startTagInTable(p, token);
  2557. }
  2558. }
  2559. else {
  2560. startTagInBody(p, token);
  2561. }
  2562. }
  2563. function endTagInCaption(p, token) {
  2564. const tn = token.tagID;
  2565. switch (tn) {
  2566. case html_js_1.TAG_ID.CAPTION:
  2567. case html_js_1.TAG_ID.TABLE: {
  2568. if (p.openElements.hasInTableScope(html_js_1.TAG_ID.CAPTION)) {
  2569. p.openElements.generateImpliedEndTags();
  2570. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.CAPTION);
  2571. p.activeFormattingElements.clearToLastMarker();
  2572. p.insertionMode = InsertionMode.IN_TABLE;
  2573. if (tn === html_js_1.TAG_ID.TABLE) {
  2574. endTagInTable(p, token);
  2575. }
  2576. }
  2577. break;
  2578. }
  2579. case html_js_1.TAG_ID.BODY:
  2580. case html_js_1.TAG_ID.COL:
  2581. case html_js_1.TAG_ID.COLGROUP:
  2582. case html_js_1.TAG_ID.HTML:
  2583. case html_js_1.TAG_ID.TBODY:
  2584. case html_js_1.TAG_ID.TD:
  2585. case html_js_1.TAG_ID.TFOOT:
  2586. case html_js_1.TAG_ID.TH:
  2587. case html_js_1.TAG_ID.THEAD:
  2588. case html_js_1.TAG_ID.TR: {
  2589. // Ignore token
  2590. break;
  2591. }
  2592. default: {
  2593. endTagInBody(p, token);
  2594. }
  2595. }
  2596. }
  2597. // The "in column group" insertion mode
  2598. //------------------------------------------------------------------
  2599. function startTagInColumnGroup(p, token) {
  2600. switch (token.tagID) {
  2601. case html_js_1.TAG_ID.HTML: {
  2602. startTagInBody(p, token);
  2603. break;
  2604. }
  2605. case html_js_1.TAG_ID.COL: {
  2606. p._appendElement(token, html_js_1.NS.HTML);
  2607. token.ackSelfClosing = true;
  2608. break;
  2609. }
  2610. case html_js_1.TAG_ID.TEMPLATE: {
  2611. startTagInHead(p, token);
  2612. break;
  2613. }
  2614. default: {
  2615. tokenInColumnGroup(p, token);
  2616. }
  2617. }
  2618. }
  2619. function endTagInColumnGroup(p, token) {
  2620. switch (token.tagID) {
  2621. case html_js_1.TAG_ID.COLGROUP: {
  2622. if (p.openElements.currentTagId === html_js_1.TAG_ID.COLGROUP) {
  2623. p.openElements.pop();
  2624. p.insertionMode = InsertionMode.IN_TABLE;
  2625. }
  2626. break;
  2627. }
  2628. case html_js_1.TAG_ID.TEMPLATE: {
  2629. templateEndTagInHead(p, token);
  2630. break;
  2631. }
  2632. case html_js_1.TAG_ID.COL: {
  2633. // Ignore token
  2634. break;
  2635. }
  2636. default: {
  2637. tokenInColumnGroup(p, token);
  2638. }
  2639. }
  2640. }
  2641. function tokenInColumnGroup(p, token) {
  2642. if (p.openElements.currentTagId === html_js_1.TAG_ID.COLGROUP) {
  2643. p.openElements.pop();
  2644. p.insertionMode = InsertionMode.IN_TABLE;
  2645. p._processToken(token);
  2646. }
  2647. }
  2648. // The "in table body" insertion mode
  2649. //------------------------------------------------------------------
  2650. function startTagInTableBody(p, token) {
  2651. switch (token.tagID) {
  2652. case html_js_1.TAG_ID.TR: {
  2653. p.openElements.clearBackToTableBodyContext();
  2654. p._insertElement(token, html_js_1.NS.HTML);
  2655. p.insertionMode = InsertionMode.IN_ROW;
  2656. break;
  2657. }
  2658. case html_js_1.TAG_ID.TH:
  2659. case html_js_1.TAG_ID.TD: {
  2660. p.openElements.clearBackToTableBodyContext();
  2661. p._insertFakeElement(html_js_1.TAG_NAMES.TR, html_js_1.TAG_ID.TR);
  2662. p.insertionMode = InsertionMode.IN_ROW;
  2663. startTagInRow(p, token);
  2664. break;
  2665. }
  2666. case html_js_1.TAG_ID.CAPTION:
  2667. case html_js_1.TAG_ID.COL:
  2668. case html_js_1.TAG_ID.COLGROUP:
  2669. case html_js_1.TAG_ID.TBODY:
  2670. case html_js_1.TAG_ID.TFOOT:
  2671. case html_js_1.TAG_ID.THEAD: {
  2672. if (p.openElements.hasTableBodyContextInTableScope()) {
  2673. p.openElements.clearBackToTableBodyContext();
  2674. p.openElements.pop();
  2675. p.insertionMode = InsertionMode.IN_TABLE;
  2676. startTagInTable(p, token);
  2677. }
  2678. break;
  2679. }
  2680. default: {
  2681. startTagInTable(p, token);
  2682. }
  2683. }
  2684. }
  2685. function endTagInTableBody(p, token) {
  2686. const tn = token.tagID;
  2687. switch (token.tagID) {
  2688. case html_js_1.TAG_ID.TBODY:
  2689. case html_js_1.TAG_ID.TFOOT:
  2690. case html_js_1.TAG_ID.THEAD: {
  2691. if (p.openElements.hasInTableScope(tn)) {
  2692. p.openElements.clearBackToTableBodyContext();
  2693. p.openElements.pop();
  2694. p.insertionMode = InsertionMode.IN_TABLE;
  2695. }
  2696. break;
  2697. }
  2698. case html_js_1.TAG_ID.TABLE: {
  2699. if (p.openElements.hasTableBodyContextInTableScope()) {
  2700. p.openElements.clearBackToTableBodyContext();
  2701. p.openElements.pop();
  2702. p.insertionMode = InsertionMode.IN_TABLE;
  2703. endTagInTable(p, token);
  2704. }
  2705. break;
  2706. }
  2707. case html_js_1.TAG_ID.BODY:
  2708. case html_js_1.TAG_ID.CAPTION:
  2709. case html_js_1.TAG_ID.COL:
  2710. case html_js_1.TAG_ID.COLGROUP:
  2711. case html_js_1.TAG_ID.HTML:
  2712. case html_js_1.TAG_ID.TD:
  2713. case html_js_1.TAG_ID.TH:
  2714. case html_js_1.TAG_ID.TR: {
  2715. // Ignore token
  2716. break;
  2717. }
  2718. default: {
  2719. endTagInTable(p, token);
  2720. }
  2721. }
  2722. }
  2723. // The "in row" insertion mode
  2724. //------------------------------------------------------------------
  2725. function startTagInRow(p, token) {
  2726. switch (token.tagID) {
  2727. case html_js_1.TAG_ID.TH:
  2728. case html_js_1.TAG_ID.TD: {
  2729. p.openElements.clearBackToTableRowContext();
  2730. p._insertElement(token, html_js_1.NS.HTML);
  2731. p.insertionMode = InsertionMode.IN_CELL;
  2732. p.activeFormattingElements.insertMarker();
  2733. break;
  2734. }
  2735. case html_js_1.TAG_ID.CAPTION:
  2736. case html_js_1.TAG_ID.COL:
  2737. case html_js_1.TAG_ID.COLGROUP:
  2738. case html_js_1.TAG_ID.TBODY:
  2739. case html_js_1.TAG_ID.TFOOT:
  2740. case html_js_1.TAG_ID.THEAD:
  2741. case html_js_1.TAG_ID.TR: {
  2742. if (p.openElements.hasInTableScope(html_js_1.TAG_ID.TR)) {
  2743. p.openElements.clearBackToTableRowContext();
  2744. p.openElements.pop();
  2745. p.insertionMode = InsertionMode.IN_TABLE_BODY;
  2746. startTagInTableBody(p, token);
  2747. }
  2748. break;
  2749. }
  2750. default: {
  2751. startTagInTable(p, token);
  2752. }
  2753. }
  2754. }
  2755. function endTagInRow(p, token) {
  2756. switch (token.tagID) {
  2757. case html_js_1.TAG_ID.TR: {
  2758. if (p.openElements.hasInTableScope(html_js_1.TAG_ID.TR)) {
  2759. p.openElements.clearBackToTableRowContext();
  2760. p.openElements.pop();
  2761. p.insertionMode = InsertionMode.IN_TABLE_BODY;
  2762. }
  2763. break;
  2764. }
  2765. case html_js_1.TAG_ID.TABLE: {
  2766. if (p.openElements.hasInTableScope(html_js_1.TAG_ID.TR)) {
  2767. p.openElements.clearBackToTableRowContext();
  2768. p.openElements.pop();
  2769. p.insertionMode = InsertionMode.IN_TABLE_BODY;
  2770. endTagInTableBody(p, token);
  2771. }
  2772. break;
  2773. }
  2774. case html_js_1.TAG_ID.TBODY:
  2775. case html_js_1.TAG_ID.TFOOT:
  2776. case html_js_1.TAG_ID.THEAD: {
  2777. if (p.openElements.hasInTableScope(token.tagID) || p.openElements.hasInTableScope(html_js_1.TAG_ID.TR)) {
  2778. p.openElements.clearBackToTableRowContext();
  2779. p.openElements.pop();
  2780. p.insertionMode = InsertionMode.IN_TABLE_BODY;
  2781. endTagInTableBody(p, token);
  2782. }
  2783. break;
  2784. }
  2785. case html_js_1.TAG_ID.BODY:
  2786. case html_js_1.TAG_ID.CAPTION:
  2787. case html_js_1.TAG_ID.COL:
  2788. case html_js_1.TAG_ID.COLGROUP:
  2789. case html_js_1.TAG_ID.HTML:
  2790. case html_js_1.TAG_ID.TD:
  2791. case html_js_1.TAG_ID.TH: {
  2792. // Ignore end tag
  2793. break;
  2794. }
  2795. default: {
  2796. endTagInTable(p, token);
  2797. }
  2798. }
  2799. }
  2800. // The "in cell" insertion mode
  2801. //------------------------------------------------------------------
  2802. function startTagInCell(p, token) {
  2803. const tn = token.tagID;
  2804. if (TABLE_VOID_ELEMENTS.has(tn)) {
  2805. if (p.openElements.hasInTableScope(html_js_1.TAG_ID.TD) || p.openElements.hasInTableScope(html_js_1.TAG_ID.TH)) {
  2806. p._closeTableCell();
  2807. startTagInRow(p, token);
  2808. }
  2809. }
  2810. else {
  2811. startTagInBody(p, token);
  2812. }
  2813. }
  2814. function endTagInCell(p, token) {
  2815. const tn = token.tagID;
  2816. switch (tn) {
  2817. case html_js_1.TAG_ID.TD:
  2818. case html_js_1.TAG_ID.TH: {
  2819. if (p.openElements.hasInTableScope(tn)) {
  2820. p.openElements.generateImpliedEndTags();
  2821. p.openElements.popUntilTagNamePopped(tn);
  2822. p.activeFormattingElements.clearToLastMarker();
  2823. p.insertionMode = InsertionMode.IN_ROW;
  2824. }
  2825. break;
  2826. }
  2827. case html_js_1.TAG_ID.TABLE:
  2828. case html_js_1.TAG_ID.TBODY:
  2829. case html_js_1.TAG_ID.TFOOT:
  2830. case html_js_1.TAG_ID.THEAD:
  2831. case html_js_1.TAG_ID.TR: {
  2832. if (p.openElements.hasInTableScope(tn)) {
  2833. p._closeTableCell();
  2834. endTagInRow(p, token);
  2835. }
  2836. break;
  2837. }
  2838. case html_js_1.TAG_ID.BODY:
  2839. case html_js_1.TAG_ID.CAPTION:
  2840. case html_js_1.TAG_ID.COL:
  2841. case html_js_1.TAG_ID.COLGROUP:
  2842. case html_js_1.TAG_ID.HTML: {
  2843. // Ignore token
  2844. break;
  2845. }
  2846. default: {
  2847. endTagInBody(p, token);
  2848. }
  2849. }
  2850. }
  2851. // The "in select" insertion mode
  2852. //------------------------------------------------------------------
  2853. function startTagInSelect(p, token) {
  2854. switch (token.tagID) {
  2855. case html_js_1.TAG_ID.HTML: {
  2856. startTagInBody(p, token);
  2857. break;
  2858. }
  2859. case html_js_1.TAG_ID.OPTION: {
  2860. if (p.openElements.currentTagId === html_js_1.TAG_ID.OPTION) {
  2861. p.openElements.pop();
  2862. }
  2863. p._insertElement(token, html_js_1.NS.HTML);
  2864. break;
  2865. }
  2866. case html_js_1.TAG_ID.OPTGROUP: {
  2867. if (p.openElements.currentTagId === html_js_1.TAG_ID.OPTION) {
  2868. p.openElements.pop();
  2869. }
  2870. if (p.openElements.currentTagId === html_js_1.TAG_ID.OPTGROUP) {
  2871. p.openElements.pop();
  2872. }
  2873. p._insertElement(token, html_js_1.NS.HTML);
  2874. break;
  2875. }
  2876. case html_js_1.TAG_ID.HR: {
  2877. if (p.openElements.currentTagId === html_js_1.TAG_ID.OPTION) {
  2878. p.openElements.pop();
  2879. }
  2880. if (p.openElements.currentTagId === html_js_1.TAG_ID.OPTGROUP) {
  2881. p.openElements.pop();
  2882. }
  2883. p._appendElement(token, html_js_1.NS.HTML);
  2884. token.ackSelfClosing = true;
  2885. break;
  2886. }
  2887. case html_js_1.TAG_ID.INPUT:
  2888. case html_js_1.TAG_ID.KEYGEN:
  2889. case html_js_1.TAG_ID.TEXTAREA:
  2890. case html_js_1.TAG_ID.SELECT: {
  2891. if (p.openElements.hasInSelectScope(html_js_1.TAG_ID.SELECT)) {
  2892. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.SELECT);
  2893. p._resetInsertionMode();
  2894. if (token.tagID !== html_js_1.TAG_ID.SELECT) {
  2895. p._processStartTag(token);
  2896. }
  2897. }
  2898. break;
  2899. }
  2900. case html_js_1.TAG_ID.SCRIPT:
  2901. case html_js_1.TAG_ID.TEMPLATE: {
  2902. startTagInHead(p, token);
  2903. break;
  2904. }
  2905. default:
  2906. // Do nothing
  2907. }
  2908. }
  2909. function endTagInSelect(p, token) {
  2910. switch (token.tagID) {
  2911. case html_js_1.TAG_ID.OPTGROUP: {
  2912. if (p.openElements.stackTop > 0 &&
  2913. p.openElements.currentTagId === html_js_1.TAG_ID.OPTION &&
  2914. p.openElements.tagIDs[p.openElements.stackTop - 1] === html_js_1.TAG_ID.OPTGROUP) {
  2915. p.openElements.pop();
  2916. }
  2917. if (p.openElements.currentTagId === html_js_1.TAG_ID.OPTGROUP) {
  2918. p.openElements.pop();
  2919. }
  2920. break;
  2921. }
  2922. case html_js_1.TAG_ID.OPTION: {
  2923. if (p.openElements.currentTagId === html_js_1.TAG_ID.OPTION) {
  2924. p.openElements.pop();
  2925. }
  2926. break;
  2927. }
  2928. case html_js_1.TAG_ID.SELECT: {
  2929. if (p.openElements.hasInSelectScope(html_js_1.TAG_ID.SELECT)) {
  2930. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.SELECT);
  2931. p._resetInsertionMode();
  2932. }
  2933. break;
  2934. }
  2935. case html_js_1.TAG_ID.TEMPLATE: {
  2936. templateEndTagInHead(p, token);
  2937. break;
  2938. }
  2939. default:
  2940. // Do nothing
  2941. }
  2942. }
  2943. // The "in select in table" insertion mode
  2944. //------------------------------------------------------------------
  2945. function startTagInSelectInTable(p, token) {
  2946. const tn = token.tagID;
  2947. if (tn === html_js_1.TAG_ID.CAPTION ||
  2948. tn === html_js_1.TAG_ID.TABLE ||
  2949. tn === html_js_1.TAG_ID.TBODY ||
  2950. tn === html_js_1.TAG_ID.TFOOT ||
  2951. tn === html_js_1.TAG_ID.THEAD ||
  2952. tn === html_js_1.TAG_ID.TR ||
  2953. tn === html_js_1.TAG_ID.TD ||
  2954. tn === html_js_1.TAG_ID.TH) {
  2955. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.SELECT);
  2956. p._resetInsertionMode();
  2957. p._processStartTag(token);
  2958. }
  2959. else {
  2960. startTagInSelect(p, token);
  2961. }
  2962. }
  2963. function endTagInSelectInTable(p, token) {
  2964. const tn = token.tagID;
  2965. if (tn === html_js_1.TAG_ID.CAPTION ||
  2966. tn === html_js_1.TAG_ID.TABLE ||
  2967. tn === html_js_1.TAG_ID.TBODY ||
  2968. tn === html_js_1.TAG_ID.TFOOT ||
  2969. tn === html_js_1.TAG_ID.THEAD ||
  2970. tn === html_js_1.TAG_ID.TR ||
  2971. tn === html_js_1.TAG_ID.TD ||
  2972. tn === html_js_1.TAG_ID.TH) {
  2973. if (p.openElements.hasInTableScope(tn)) {
  2974. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.SELECT);
  2975. p._resetInsertionMode();
  2976. p.onEndTag(token);
  2977. }
  2978. }
  2979. else {
  2980. endTagInSelect(p, token);
  2981. }
  2982. }
  2983. // The "in template" insertion mode
  2984. //------------------------------------------------------------------
  2985. function startTagInTemplate(p, token) {
  2986. switch (token.tagID) {
  2987. // First, handle tags that can start without a mode change
  2988. case html_js_1.TAG_ID.BASE:
  2989. case html_js_1.TAG_ID.BASEFONT:
  2990. case html_js_1.TAG_ID.BGSOUND:
  2991. case html_js_1.TAG_ID.LINK:
  2992. case html_js_1.TAG_ID.META:
  2993. case html_js_1.TAG_ID.NOFRAMES:
  2994. case html_js_1.TAG_ID.SCRIPT:
  2995. case html_js_1.TAG_ID.STYLE:
  2996. case html_js_1.TAG_ID.TEMPLATE:
  2997. case html_js_1.TAG_ID.TITLE: {
  2998. startTagInHead(p, token);
  2999. break;
  3000. }
  3001. // Re-process the token in the appropriate mode
  3002. case html_js_1.TAG_ID.CAPTION:
  3003. case html_js_1.TAG_ID.COLGROUP:
  3004. case html_js_1.TAG_ID.TBODY:
  3005. case html_js_1.TAG_ID.TFOOT:
  3006. case html_js_1.TAG_ID.THEAD: {
  3007. p.tmplInsertionModeStack[0] = InsertionMode.IN_TABLE;
  3008. p.insertionMode = InsertionMode.IN_TABLE;
  3009. startTagInTable(p, token);
  3010. break;
  3011. }
  3012. case html_js_1.TAG_ID.COL: {
  3013. p.tmplInsertionModeStack[0] = InsertionMode.IN_COLUMN_GROUP;
  3014. p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
  3015. startTagInColumnGroup(p, token);
  3016. break;
  3017. }
  3018. case html_js_1.TAG_ID.TR: {
  3019. p.tmplInsertionModeStack[0] = InsertionMode.IN_TABLE_BODY;
  3020. p.insertionMode = InsertionMode.IN_TABLE_BODY;
  3021. startTagInTableBody(p, token);
  3022. break;
  3023. }
  3024. case html_js_1.TAG_ID.TD:
  3025. case html_js_1.TAG_ID.TH: {
  3026. p.tmplInsertionModeStack[0] = InsertionMode.IN_ROW;
  3027. p.insertionMode = InsertionMode.IN_ROW;
  3028. startTagInRow(p, token);
  3029. break;
  3030. }
  3031. default: {
  3032. p.tmplInsertionModeStack[0] = InsertionMode.IN_BODY;
  3033. p.insertionMode = InsertionMode.IN_BODY;
  3034. startTagInBody(p, token);
  3035. }
  3036. }
  3037. }
  3038. function endTagInTemplate(p, token) {
  3039. if (token.tagID === html_js_1.TAG_ID.TEMPLATE) {
  3040. templateEndTagInHead(p, token);
  3041. }
  3042. }
  3043. function eofInTemplate(p, token) {
  3044. if (p.openElements.tmplCount > 0) {
  3045. p.openElements.popUntilTagNamePopped(html_js_1.TAG_ID.TEMPLATE);
  3046. p.activeFormattingElements.clearToLastMarker();
  3047. p.tmplInsertionModeStack.shift();
  3048. p._resetInsertionMode();
  3049. p.onEof(token);
  3050. }
  3051. else {
  3052. stopParsing(p, token);
  3053. }
  3054. }
  3055. // The "after body" insertion mode
  3056. //------------------------------------------------------------------
  3057. function startTagAfterBody(p, token) {
  3058. if (token.tagID === html_js_1.TAG_ID.HTML) {
  3059. startTagInBody(p, token);
  3060. }
  3061. else {
  3062. tokenAfterBody(p, token);
  3063. }
  3064. }
  3065. function endTagAfterBody(p, token) {
  3066. var _a;
  3067. if (token.tagID === html_js_1.TAG_ID.HTML) {
  3068. if (!p.fragmentContext) {
  3069. p.insertionMode = InsertionMode.AFTER_AFTER_BODY;
  3070. }
  3071. //NOTE: <html> is never popped from the stack, so we need to updated
  3072. //the end location explicitly.
  3073. if (p.options.sourceCodeLocationInfo && p.openElements.tagIDs[0] === html_js_1.TAG_ID.HTML) {
  3074. p._setEndLocation(p.openElements.items[0], token);
  3075. // Update the body element, if it doesn't have an end tag
  3076. const bodyElement = p.openElements.items[1];
  3077. if (bodyElement && !((_a = p.treeAdapter.getNodeSourceCodeLocation(bodyElement)) === null || _a === void 0 ? void 0 : _a.endTag)) {
  3078. p._setEndLocation(bodyElement, token);
  3079. }
  3080. }
  3081. }
  3082. else {
  3083. tokenAfterBody(p, token);
  3084. }
  3085. }
  3086. function tokenAfterBody(p, token) {
  3087. p.insertionMode = InsertionMode.IN_BODY;
  3088. modeInBody(p, token);
  3089. }
  3090. // The "in frameset" insertion mode
  3091. //------------------------------------------------------------------
  3092. function startTagInFrameset(p, token) {
  3093. switch (token.tagID) {
  3094. case html_js_1.TAG_ID.HTML: {
  3095. startTagInBody(p, token);
  3096. break;
  3097. }
  3098. case html_js_1.TAG_ID.FRAMESET: {
  3099. p._insertElement(token, html_js_1.NS.HTML);
  3100. break;
  3101. }
  3102. case html_js_1.TAG_ID.FRAME: {
  3103. p._appendElement(token, html_js_1.NS.HTML);
  3104. token.ackSelfClosing = true;
  3105. break;
  3106. }
  3107. case html_js_1.TAG_ID.NOFRAMES: {
  3108. startTagInHead(p, token);
  3109. break;
  3110. }
  3111. default:
  3112. // Do nothing
  3113. }
  3114. }
  3115. function endTagInFrameset(p, token) {
  3116. if (token.tagID === html_js_1.TAG_ID.FRAMESET && !p.openElements.isRootHtmlElementCurrent()) {
  3117. p.openElements.pop();
  3118. if (!p.fragmentContext && p.openElements.currentTagId !== html_js_1.TAG_ID.FRAMESET) {
  3119. p.insertionMode = InsertionMode.AFTER_FRAMESET;
  3120. }
  3121. }
  3122. }
  3123. // The "after frameset" insertion mode
  3124. //------------------------------------------------------------------
  3125. function startTagAfterFrameset(p, token) {
  3126. switch (token.tagID) {
  3127. case html_js_1.TAG_ID.HTML: {
  3128. startTagInBody(p, token);
  3129. break;
  3130. }
  3131. case html_js_1.TAG_ID.NOFRAMES: {
  3132. startTagInHead(p, token);
  3133. break;
  3134. }
  3135. default:
  3136. // Do nothing
  3137. }
  3138. }
  3139. function endTagAfterFrameset(p, token) {
  3140. if (token.tagID === html_js_1.TAG_ID.HTML) {
  3141. p.insertionMode = InsertionMode.AFTER_AFTER_FRAMESET;
  3142. }
  3143. }
  3144. // The "after after body" insertion mode
  3145. //------------------------------------------------------------------
  3146. function startTagAfterAfterBody(p, token) {
  3147. if (token.tagID === html_js_1.TAG_ID.HTML) {
  3148. startTagInBody(p, token);
  3149. }
  3150. else {
  3151. tokenAfterAfterBody(p, token);
  3152. }
  3153. }
  3154. function tokenAfterAfterBody(p, token) {
  3155. p.insertionMode = InsertionMode.IN_BODY;
  3156. modeInBody(p, token);
  3157. }
  3158. // The "after after frameset" insertion mode
  3159. //------------------------------------------------------------------
  3160. function startTagAfterAfterFrameset(p, token) {
  3161. switch (token.tagID) {
  3162. case html_js_1.TAG_ID.HTML: {
  3163. startTagInBody(p, token);
  3164. break;
  3165. }
  3166. case html_js_1.TAG_ID.NOFRAMES: {
  3167. startTagInHead(p, token);
  3168. break;
  3169. }
  3170. default:
  3171. // Do nothing
  3172. }
  3173. }
  3174. // The rules for parsing tokens in foreign content
  3175. //------------------------------------------------------------------
  3176. function nullCharacterInForeignContent(p, token) {
  3177. token.chars = unicode.REPLACEMENT_CHARACTER;
  3178. p._insertCharacters(token);
  3179. }
  3180. function characterInForeignContent(p, token) {
  3181. p._insertCharacters(token);
  3182. p.framesetOk = false;
  3183. }
  3184. function popUntilHtmlOrIntegrationPoint(p) {
  3185. while (p.treeAdapter.getNamespaceURI(p.openElements.current) !== html_js_1.NS.HTML &&
  3186. !p._isIntegrationPoint(p.openElements.currentTagId, p.openElements.current)) {
  3187. p.openElements.pop();
  3188. }
  3189. }
  3190. function startTagInForeignContent(p, token) {
  3191. if (foreignContent.causesExit(token)) {
  3192. popUntilHtmlOrIntegrationPoint(p);
  3193. p._startTagOutsideForeignContent(token);
  3194. }
  3195. else {
  3196. const current = p._getAdjustedCurrentElement();
  3197. const currentNs = p.treeAdapter.getNamespaceURI(current);
  3198. if (currentNs === html_js_1.NS.MATHML) {
  3199. foreignContent.adjustTokenMathMLAttrs(token);
  3200. }
  3201. else if (currentNs === html_js_1.NS.SVG) {
  3202. foreignContent.adjustTokenSVGTagName(token);
  3203. foreignContent.adjustTokenSVGAttrs(token);
  3204. }
  3205. foreignContent.adjustTokenXMLAttrs(token);
  3206. if (token.selfClosing) {
  3207. p._appendElement(token, currentNs);
  3208. }
  3209. else {
  3210. p._insertElement(token, currentNs);
  3211. }
  3212. token.ackSelfClosing = true;
  3213. }
  3214. }
  3215. function endTagInForeignContent(p, token) {
  3216. if (token.tagID === html_js_1.TAG_ID.P || token.tagID === html_js_1.TAG_ID.BR) {
  3217. popUntilHtmlOrIntegrationPoint(p);
  3218. p._endTagOutsideForeignContent(token);
  3219. return;
  3220. }
  3221. for (let i = p.openElements.stackTop; i > 0; i--) {
  3222. const element = p.openElements.items[i];
  3223. if (p.treeAdapter.getNamespaceURI(element) === html_js_1.NS.HTML) {
  3224. p._endTagOutsideForeignContent(token);
  3225. break;
  3226. }
  3227. const tagName = p.treeAdapter.getTagName(element);
  3228. if (tagName.toLowerCase() === token.tagName) {
  3229. //NOTE: update the token tag name for `_setEndLocation`.
  3230. token.tagName = tagName;
  3231. p.openElements.shortenToLength(i);
  3232. break;
  3233. }
  3234. }
  3235. }