HTMLTokenizer.cpp 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <LibWeb/Parser/HTMLToken.h>
  27. #include <LibWeb/Parser/HTMLTokenizer.h>
  28. #include <ctype.h>
  29. #pragma GCC diagnostic ignored "-Wunused-label"
  30. //#define TOKENIZER_TRACE
  31. #define TODO() \
  32. do { \
  33. dbg() << "[TODO: " << state_name(m_state) << "] '" << (char)current_input_character.value() << "'"; \
  34. ASSERT_NOT_REACHED(); \
  35. } while (0)
  36. #define SWITCH_TO(new_state) \
  37. do { \
  38. will_switch_to(State::new_state); \
  39. m_state = State::new_state; \
  40. current_input_character = next_codepoint(); \
  41. goto new_state; \
  42. } while (0)
  43. #define RECONSUME_IN(new_state) \
  44. do { \
  45. will_reconsume_in(State::new_state); \
  46. m_state = State::new_state; \
  47. goto new_state; \
  48. } while (0)
  49. #define SWITCH_TO_AND_EMIT_CURRENT_TOKEN(new_state) \
  50. do { \
  51. will_switch_to(State::new_state); \
  52. m_state = State::new_state; \
  53. will_emit(m_current_token); \
  54. m_queued_tokens.enqueue(m_current_token); \
  55. return m_queued_tokens.dequeue(); \
  56. } while (0)
  57. #define EMIT_CHARACTER_AND_RECONSUME_IN(codepoint, new_state) \
  58. do { \
  59. m_queued_tokens.enqueue(m_current_token); \
  60. will_reconsume_in(State::new_state); \
  61. m_state = State::new_state; \
  62. goto new_state; \
  63. } while (0)
  64. #define DONT_CONSUME_NEXT_INPUT_CHARACTER --m_cursor;
  65. #define ON(codepoint) \
  66. if (current_input_character.has_value() && current_input_character.value() == codepoint)
  67. #define ON_EOF \
  68. if (!current_input_character.has_value())
  69. #define ON_ASCII_ALPHA \
  70. if (current_input_character.has_value() && isalpha(current_input_character.value()))
  71. #define ON_ASCII_UPPER_ALPHA \
  72. if (current_input_character.has_value() && current_input_character.value() >= 'A' && current_input_character.value() <= 'Z')
  73. #define ON_ASCII_LOWER_ALPHA \
  74. if (current_input_character.has_value() && current_input_character.value() >= 'a' && current_input_character.value() <= 'z')
  75. #define ON_WHITESPACE \
  76. if (current_input_character.has_value() && (current_input_character.value() == '\t' || current_input_character.value() == '\n' || current_input_character.value() == '\f' || current_input_character.value() == ' '))
  77. #define ANYTHING_ELSE if (1)
  78. #define EMIT_EOF \
  79. do { \
  80. if (m_has_emitted_eof) \
  81. return {}; \
  82. m_has_emitted_eof = true; \
  83. create_new_token(HTMLToken::Type::EndOfFile); \
  84. will_emit(m_current_token); \
  85. m_queued_tokens.enqueue(m_current_token); \
  86. return m_queued_tokens.dequeue(); \
  87. } while (0)
  88. #define EMIT_CURRENT_TOKEN \
  89. do { \
  90. will_emit(m_current_token); \
  91. m_queued_tokens.enqueue(m_current_token); \
  92. return m_queued_tokens.dequeue(); \
  93. } while (0)
  94. #define EMIT_CHARACTER(codepoint) \
  95. do { \
  96. create_new_token(HTMLToken::Type::Character); \
  97. m_current_token.m_comment_or_character.data.append(codepoint); \
  98. m_queued_tokens.enqueue(m_current_token); \
  99. return m_queued_tokens.dequeue(); \
  100. } while (0)
  101. #define EMIT_CURRENT_CHARACTER \
  102. EMIT_CHARACTER(current_input_character.value());
  103. #define BEGIN_STATE(state) \
  104. state: \
  105. case State::state: { \
  106. { \
  107. {
  108. #define END_STATE \
  109. ASSERT_NOT_REACHED(); \
  110. break; \
  111. } \
  112. } \
  113. }
  114. namespace Web {
  115. Optional<u32> HTMLTokenizer::next_codepoint()
  116. {
  117. if (m_cursor >= m_input.length())
  118. return {};
  119. return m_input[m_cursor++];
  120. }
  121. Optional<u32> HTMLTokenizer::peek_codepoint(size_t offset) const
  122. {
  123. if ((m_cursor + offset) >= m_input.length())
  124. return {};
  125. return m_input[m_cursor + offset];
  126. }
  127. Optional<HTMLToken> HTMLTokenizer::next_token()
  128. {
  129. if (!m_queued_tokens.is_empty())
  130. return m_queued_tokens.dequeue();
  131. for (;;) {
  132. auto current_input_character = next_codepoint();
  133. switch (m_state) {
  134. BEGIN_STATE(Data)
  135. {
  136. ON('&')
  137. {
  138. m_return_state = State::Data;
  139. SWITCH_TO(CharacterReference);
  140. }
  141. ON('<')
  142. {
  143. SWITCH_TO(TagOpen);
  144. }
  145. ON_EOF
  146. {
  147. EMIT_EOF;
  148. }
  149. ANYTHING_ELSE
  150. {
  151. EMIT_CURRENT_CHARACTER;
  152. }
  153. }
  154. END_STATE
  155. BEGIN_STATE(TagOpen)
  156. {
  157. ON('!')
  158. {
  159. SWITCH_TO(MarkupDeclarationOpen);
  160. }
  161. ON('/')
  162. {
  163. SWITCH_TO(EndTagOpen);
  164. }
  165. ON_ASCII_ALPHA
  166. {
  167. create_new_token(HTMLToken::Type::StartTag);
  168. RECONSUME_IN(TagName);
  169. }
  170. ON('?')
  171. {
  172. TODO();
  173. }
  174. ANYTHING_ELSE
  175. {
  176. TODO();
  177. }
  178. }
  179. END_STATE
  180. BEGIN_STATE(TagName)
  181. {
  182. ON_WHITESPACE
  183. {
  184. SWITCH_TO(BeforeAttributeName);
  185. }
  186. ON('/')
  187. {
  188. SWITCH_TO(SelfClosingStartTag);
  189. }
  190. ON('>')
  191. {
  192. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  193. }
  194. ANYTHING_ELSE
  195. {
  196. m_current_token.m_tag.tag_name.append(current_input_character.value());
  197. continue;
  198. }
  199. }
  200. END_STATE
  201. BEGIN_STATE(EndTagOpen)
  202. {
  203. ON_ASCII_ALPHA
  204. {
  205. create_new_token(HTMLToken::Type::EndTag);
  206. RECONSUME_IN(TagName);
  207. }
  208. }
  209. END_STATE
  210. BEGIN_STATE(MarkupDeclarationOpen)
  211. {
  212. DONT_CONSUME_NEXT_INPUT_CHARACTER;
  213. if (consume_next_if_match("--")) {
  214. create_new_token(HTMLToken::Type::Comment);
  215. SWITCH_TO(CommentStart);
  216. }
  217. if (consume_next_if_match("DOCTYPE", CaseSensitivity::CaseInsensitive)) {
  218. SWITCH_TO(DOCTYPE);
  219. }
  220. }
  221. END_STATE
  222. BEGIN_STATE(DOCTYPE)
  223. {
  224. ON_WHITESPACE
  225. {
  226. SWITCH_TO(BeforeDOCTYPEName);
  227. }
  228. ON('>')
  229. {
  230. RECONSUME_IN(BeforeDOCTYPEName);
  231. }
  232. ON_EOF
  233. {
  234. TODO();
  235. }
  236. ANYTHING_ELSE
  237. {
  238. TODO();
  239. }
  240. }
  241. END_STATE
  242. BEGIN_STATE(BeforeDOCTYPEName)
  243. {
  244. ON_WHITESPACE
  245. {
  246. continue;
  247. }
  248. ON_ASCII_UPPER_ALPHA
  249. {
  250. create_new_token(HTMLToken::Type::DOCTYPE);
  251. m_current_token.m_doctype.name.append(tolower(current_input_character.value()));
  252. SWITCH_TO(DOCTYPEName);
  253. }
  254. ON(0)
  255. {
  256. TODO();
  257. }
  258. ON('>')
  259. {
  260. TODO();
  261. }
  262. ON_EOF
  263. {
  264. TODO();
  265. }
  266. ANYTHING_ELSE
  267. {
  268. create_new_token(HTMLToken::Type::DOCTYPE);
  269. m_current_token.m_doctype.name.append(current_input_character.value());
  270. SWITCH_TO(DOCTYPEName);
  271. }
  272. }
  273. END_STATE
  274. BEGIN_STATE(DOCTYPEName)
  275. {
  276. ON_WHITESPACE
  277. {
  278. SWITCH_TO(AfterDOCTYPEName);
  279. }
  280. ON('>')
  281. {
  282. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  283. }
  284. ON_ASCII_UPPER_ALPHA
  285. {
  286. m_current_token.m_doctype.name.append(tolower(current_input_character.value()));
  287. }
  288. ON(0)
  289. {
  290. TODO();
  291. }
  292. ON_EOF
  293. {
  294. TODO();
  295. }
  296. ANYTHING_ELSE
  297. {
  298. m_current_token.m_doctype.name.append(current_input_character.value());
  299. continue;
  300. }
  301. }
  302. END_STATE
  303. BEGIN_STATE(AfterDOCTYPEName)
  304. {
  305. ON_WHITESPACE
  306. {
  307. continue;
  308. }
  309. ON('>')
  310. {
  311. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  312. }
  313. ON_EOF
  314. {
  315. TODO();
  316. }
  317. ANYTHING_ELSE
  318. {
  319. if (toupper(current_input_character.value()) == 'P' && consume_next_if_match("UBLIC", CaseSensitivity::CaseInsensitive)) {
  320. SWITCH_TO(AfterDOCTYPEPublicKeyword);
  321. }
  322. if (toupper(current_input_character.value()) == 'S' && consume_next_if_match("YSTEM", CaseSensitivity::CaseInsensitive)) {
  323. SWITCH_TO(AfterDOCTYPESystemKeyword);
  324. }
  325. TODO();
  326. }
  327. }
  328. END_STATE
  329. BEGIN_STATE(AfterDOCTYPEPublicKeyword)
  330. {
  331. ON_WHITESPACE
  332. {
  333. SWITCH_TO(BeforeDOCTYPEPublicIdentifier);
  334. }
  335. ON('"')
  336. {
  337. TODO();
  338. }
  339. ON('\'')
  340. {
  341. TODO();
  342. }
  343. ON('>')
  344. {
  345. TODO();
  346. }
  347. ON_EOF
  348. {
  349. TODO();
  350. }
  351. ANYTHING_ELSE
  352. {
  353. TODO();
  354. }
  355. }
  356. END_STATE
  357. BEGIN_STATE(AfterDOCTYPESystemKeyword)
  358. {
  359. ON_WHITESPACE
  360. {
  361. SWITCH_TO(BeforeDOCTYPESystemIdentifier);
  362. }
  363. ON('"')
  364. {
  365. TODO();
  366. }
  367. ON('\'')
  368. {
  369. TODO();
  370. }
  371. ON('>')
  372. {
  373. TODO();
  374. }
  375. ON_EOF
  376. {
  377. TODO();
  378. }
  379. ANYTHING_ELSE
  380. {
  381. TODO();
  382. }
  383. }
  384. END_STATE
  385. BEGIN_STATE(BeforeDOCTYPEPublicIdentifier)
  386. {
  387. ON_WHITESPACE
  388. {
  389. continue;
  390. }
  391. ON('"')
  392. {
  393. m_current_token.m_doctype.public_identifier.clear();
  394. SWITCH_TO(DOCTYPEPublicIdentifierDoubleQuoted);
  395. }
  396. ON('\'')
  397. {
  398. m_current_token.m_doctype.public_identifier.clear();
  399. SWITCH_TO(DOCTYPEPublicIdentifierSingleQuoted);
  400. }
  401. ON('>')
  402. {
  403. TODO();
  404. }
  405. ON_EOF
  406. {
  407. TODO();
  408. }
  409. ANYTHING_ELSE
  410. {
  411. TODO();
  412. }
  413. }
  414. END_STATE
  415. BEGIN_STATE(BeforeDOCTYPESystemIdentifier)
  416. {
  417. ON_WHITESPACE
  418. {
  419. continue;
  420. }
  421. ON('"')
  422. {
  423. m_current_token.m_doctype.system_identifier.clear();
  424. SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted);
  425. }
  426. ON('\'')
  427. {
  428. m_current_token.m_doctype.system_identifier.clear();
  429. SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted);
  430. }
  431. ON('>')
  432. {
  433. TODO();
  434. }
  435. ON_EOF
  436. {
  437. TODO();
  438. }
  439. ANYTHING_ELSE
  440. {
  441. TODO();
  442. }
  443. }
  444. END_STATE
  445. BEGIN_STATE(DOCTYPEPublicIdentifierDoubleQuoted)
  446. {
  447. ON('"')
  448. {
  449. SWITCH_TO(AfterDOCTYPEPublicIdentifier);
  450. }
  451. ON(0)
  452. {
  453. TODO();
  454. }
  455. ON('>')
  456. {
  457. TODO();
  458. }
  459. ON_EOF
  460. {
  461. TODO();
  462. }
  463. ANYTHING_ELSE
  464. {
  465. m_current_token.m_doctype.public_identifier.append(current_input_character.value());
  466. continue;
  467. }
  468. }
  469. END_STATE
  470. BEGIN_STATE(DOCTYPEPublicIdentifierSingleQuoted)
  471. {
  472. ON('\'')
  473. {
  474. SWITCH_TO(AfterDOCTYPEPublicIdentifier);
  475. }
  476. ON(0)
  477. {
  478. TODO();
  479. }
  480. ON('>')
  481. {
  482. TODO();
  483. }
  484. ON_EOF
  485. {
  486. TODO();
  487. }
  488. ANYTHING_ELSE
  489. {
  490. m_current_token.m_doctype.public_identifier.append(current_input_character.value());
  491. continue;
  492. }
  493. }
  494. END_STATE
  495. BEGIN_STATE(DOCTYPESystemIdentifierDoubleQuoted)
  496. {
  497. ON('"')
  498. {
  499. SWITCH_TO(AfterDOCTYPESystemIdentifier);
  500. }
  501. ON(0)
  502. {
  503. TODO();
  504. }
  505. ON('>')
  506. {
  507. TODO();
  508. }
  509. ON_EOF
  510. {
  511. TODO();
  512. }
  513. ANYTHING_ELSE
  514. {
  515. m_current_token.m_doctype.system_identifier.append(current_input_character.value());
  516. continue;
  517. }
  518. }
  519. END_STATE
  520. BEGIN_STATE(DOCTYPESystemIdentifierSingleQuoted)
  521. {
  522. ON('\'')
  523. {
  524. SWITCH_TO(AfterDOCTYPESystemIdentifier);
  525. }
  526. ON(0)
  527. {
  528. TODO();
  529. }
  530. ON('>')
  531. {
  532. TODO();
  533. }
  534. ON_EOF
  535. {
  536. TODO();
  537. }
  538. ANYTHING_ELSE
  539. {
  540. m_current_token.m_doctype.system_identifier.append(current_input_character.value());
  541. continue;
  542. }
  543. }
  544. END_STATE
  545. BEGIN_STATE(AfterDOCTYPEPublicIdentifier)
  546. {
  547. ON_WHITESPACE
  548. {
  549. SWITCH_TO(BetweenDOCTYPEPublicAndSystemIdentifiers);
  550. }
  551. ON('>')
  552. {
  553. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  554. }
  555. ON('"')
  556. {
  557. TODO();
  558. }
  559. ON('\'')
  560. {
  561. TODO();
  562. }
  563. ON_EOF
  564. {
  565. TODO();
  566. }
  567. ANYTHING_ELSE
  568. {
  569. TODO();
  570. }
  571. }
  572. END_STATE
  573. BEGIN_STATE(BetweenDOCTYPEPublicAndSystemIdentifiers)
  574. {
  575. ON_WHITESPACE
  576. {
  577. continue;
  578. }
  579. ON('>')
  580. {
  581. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  582. }
  583. ON('"')
  584. {
  585. m_current_token.m_doctype.system_identifier.clear();
  586. SWITCH_TO(DOCTYPESystemIdentifierDoubleQuoted);
  587. }
  588. ON('\'')
  589. {
  590. m_current_token.m_doctype.system_identifier.clear();
  591. SWITCH_TO(DOCTYPESystemIdentifierSingleQuoted);
  592. }
  593. ON_EOF
  594. {
  595. TODO();
  596. }
  597. ANYTHING_ELSE
  598. {
  599. TODO();
  600. }
  601. }
  602. END_STATE
  603. BEGIN_STATE(AfterDOCTYPESystemIdentifier)
  604. {
  605. ON_WHITESPACE
  606. {
  607. continue;
  608. }
  609. ON('>')
  610. {
  611. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  612. }
  613. ON_EOF
  614. {
  615. TODO();
  616. }
  617. ANYTHING_ELSE
  618. {
  619. TODO();
  620. }
  621. }
  622. END_STATE
  623. BEGIN_STATE(BeforeAttributeName)
  624. {
  625. ON_WHITESPACE
  626. {
  627. continue;
  628. }
  629. ON('/')
  630. {
  631. RECONSUME_IN(AfterAttributeName);
  632. }
  633. ON('>')
  634. {
  635. RECONSUME_IN(AfterAttributeName);
  636. }
  637. ON_EOF
  638. {
  639. RECONSUME_IN(AfterAttributeName);
  640. }
  641. ON('=')
  642. {
  643. TODO();
  644. }
  645. ANYTHING_ELSE
  646. {
  647. m_current_token.m_tag.attributes.append(HTMLToken::AttributeBuilder());
  648. RECONSUME_IN(AttributeName);
  649. }
  650. }
  651. END_STATE
  652. BEGIN_STATE(SelfClosingStartTag)
  653. {
  654. }
  655. END_STATE
  656. BEGIN_STATE(AttributeName)
  657. {
  658. ON_WHITESPACE
  659. {
  660. RECONSUME_IN(AfterAttributeName);
  661. }
  662. ON('/')
  663. {
  664. RECONSUME_IN(AfterAttributeName);
  665. }
  666. ON('>')
  667. {
  668. RECONSUME_IN(AfterAttributeName);
  669. }
  670. ON_EOF
  671. {
  672. RECONSUME_IN(AfterAttributeName);
  673. }
  674. ON('=')
  675. {
  676. SWITCH_TO(BeforeAttributeValue);
  677. }
  678. ANYTHING_ELSE
  679. {
  680. m_current_token.m_tag.attributes.last().name_builder.append(current_input_character.value());
  681. continue;
  682. }
  683. }
  684. END_STATE
  685. BEGIN_STATE(AfterAttributeName)
  686. {
  687. }
  688. END_STATE
  689. BEGIN_STATE(BeforeAttributeValue)
  690. {
  691. ON_WHITESPACE
  692. {
  693. continue;
  694. }
  695. ON('"')
  696. {
  697. SWITCH_TO(AttributeValueDoubleQuoted);
  698. }
  699. ON('\'')
  700. {
  701. SWITCH_TO(AttributeValueSingleQuoted);
  702. }
  703. ON('>')
  704. {
  705. TODO();
  706. }
  707. ANYTHING_ELSE
  708. {
  709. RECONSUME_IN(AttributeValueUnquoted);
  710. }
  711. }
  712. END_STATE
  713. BEGIN_STATE(AttributeValueDoubleQuoted)
  714. {
  715. ON('"')
  716. {
  717. SWITCH_TO(AfterAttributeValueQuoted);
  718. }
  719. ON('&')
  720. {
  721. m_return_state = State::AttributeValueDoubleQuoted;
  722. SWITCH_TO(CharacterReference);
  723. }
  724. ON(0)
  725. {
  726. TODO();
  727. }
  728. ON_EOF
  729. {
  730. TODO();
  731. }
  732. ANYTHING_ELSE
  733. {
  734. m_current_token.m_tag.attributes.last().value_builder.append(current_input_character.value());
  735. continue;
  736. }
  737. }
  738. END_STATE
  739. BEGIN_STATE(AttributeValueSingleQuoted)
  740. {
  741. ON('\'')
  742. {
  743. SWITCH_TO(AfterAttributeValueQuoted);
  744. }
  745. ON('&')
  746. {
  747. m_return_state = State::AttributeValueSingleQuoted;
  748. SWITCH_TO(CharacterReference);
  749. }
  750. ON(0)
  751. {
  752. TODO();
  753. }
  754. ON_EOF
  755. {
  756. TODO();
  757. }
  758. ANYTHING_ELSE
  759. {
  760. m_current_token.m_tag.attributes.last().value_builder.append(current_input_character.value());
  761. continue;
  762. }
  763. }
  764. END_STATE
  765. BEGIN_STATE(AttributeValueUnquoted)
  766. {
  767. ON_WHITESPACE
  768. {
  769. SWITCH_TO(BeforeAttributeName);
  770. }
  771. ON('&')
  772. {
  773. m_return_state = State::AttributeValueUnquoted;
  774. SWITCH_TO(CharacterReference);
  775. }
  776. ON('>')
  777. {
  778. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  779. }
  780. ON(0)
  781. {
  782. TODO();
  783. }
  784. ON_EOF
  785. {
  786. TODO();
  787. }
  788. ANYTHING_ELSE
  789. {
  790. m_current_token.m_tag.attributes.last().value_builder.append(current_input_character.value());
  791. continue;
  792. }
  793. }
  794. END_STATE
  795. BEGIN_STATE(AfterAttributeValueQuoted)
  796. {
  797. ON_WHITESPACE
  798. {
  799. SWITCH_TO(BeforeAttributeName);
  800. }
  801. ON('/')
  802. {
  803. SWITCH_TO(SelfClosingStartTag);
  804. }
  805. ON('>')
  806. {
  807. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  808. }
  809. ON_EOF
  810. {
  811. TODO();
  812. }
  813. ANYTHING_ELSE
  814. {
  815. TODO();
  816. }
  817. }
  818. END_STATE
  819. BEGIN_STATE(CommentStart)
  820. {
  821. ON('-')
  822. {
  823. SWITCH_TO(CommentStartDash);
  824. }
  825. ON('>')
  826. {
  827. TODO();
  828. }
  829. ANYTHING_ELSE
  830. {
  831. RECONSUME_IN(Comment);
  832. }
  833. }
  834. END_STATE
  835. BEGIN_STATE(CommentStartDash)
  836. {
  837. ON('-')
  838. {
  839. SWITCH_TO(CommentEnd);
  840. }
  841. ON('>')
  842. {
  843. TODO();
  844. }
  845. ON_EOF
  846. {
  847. TODO();
  848. }
  849. ANYTHING_ELSE
  850. {
  851. m_current_token.m_comment_or_character.data.append('-');
  852. RECONSUME_IN(Comment);
  853. }
  854. }
  855. END_STATE
  856. BEGIN_STATE(Comment)
  857. {
  858. ON('<')
  859. {
  860. m_current_token.m_comment_or_character.data.append(current_input_character.value());
  861. SWITCH_TO(CommentLessThanSign);
  862. }
  863. ON('-')
  864. {
  865. SWITCH_TO(CommentEndDash);
  866. }
  867. ON(0)
  868. {
  869. TODO();
  870. }
  871. ON_EOF
  872. {
  873. TODO();
  874. }
  875. ANYTHING_ELSE
  876. {
  877. m_current_token.m_comment_or_character.data.append(current_input_character.value());
  878. continue;
  879. }
  880. }
  881. END_STATE
  882. BEGIN_STATE(CommentEnd)
  883. {
  884. ON('>')
  885. {
  886. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  887. }
  888. ON('!')
  889. {
  890. SWITCH_TO(CommentEndBang);
  891. }
  892. ON('-')
  893. {
  894. m_current_token.m_comment_or_character.data.append('-');
  895. continue;
  896. }
  897. ON_EOF
  898. {
  899. TODO();
  900. }
  901. ANYTHING_ELSE
  902. {
  903. m_current_token.m_comment_or_character.data.append('-');
  904. RECONSUME_IN(Comment);
  905. }
  906. }
  907. END_STATE
  908. BEGIN_STATE(CommentEndBang)
  909. {
  910. ON('-')
  911. {
  912. m_current_token.m_comment_or_character.data.append("--!");
  913. SWITCH_TO(CommentEndDash);
  914. }
  915. ON('>')
  916. {
  917. TODO();
  918. }
  919. ON_EOF
  920. {
  921. TODO();
  922. }
  923. ANYTHING_ELSE
  924. {
  925. m_current_token.m_comment_or_character.data.append("--!");
  926. RECONSUME_IN(Comment);
  927. }
  928. }
  929. END_STATE
  930. BEGIN_STATE(CommentEndDash)
  931. {
  932. ON('-')
  933. {
  934. SWITCH_TO(CommentEnd);
  935. }
  936. ON_EOF
  937. {
  938. TODO();
  939. }
  940. ANYTHING_ELSE
  941. {
  942. m_current_token.m_comment_or_character.data.append('-');
  943. RECONSUME_IN(Comment);
  944. }
  945. }
  946. END_STATE
  947. BEGIN_STATE(CommentLessThanSign)
  948. {
  949. ON('!')
  950. {
  951. m_current_token.m_comment_or_character.data.append(current_input_character.value());
  952. SWITCH_TO(CommentLessThanSignBang);
  953. }
  954. ON('<')
  955. {
  956. m_current_token.m_comment_or_character.data.append(current_input_character.value());
  957. continue;
  958. }
  959. ANYTHING_ELSE
  960. {
  961. RECONSUME_IN(Comment);
  962. }
  963. }
  964. END_STATE
  965. BEGIN_STATE(CommentLessThanSignBang)
  966. {
  967. ON('-')
  968. {
  969. SWITCH_TO(CommentLessThanSignBangDash);
  970. }
  971. ANYTHING_ELSE
  972. {
  973. RECONSUME_IN(Comment);
  974. }
  975. }
  976. END_STATE
  977. BEGIN_STATE(CommentLessThanSignBangDash)
  978. {
  979. ON('-')
  980. {
  981. SWITCH_TO(CommentLessThanSignBangDashDash);
  982. }
  983. ANYTHING_ELSE
  984. {
  985. RECONSUME_IN(Comment);
  986. }
  987. }
  988. END_STATE
  989. BEGIN_STATE(CommentLessThanSignBangDashDash)
  990. {
  991. ON('>')
  992. {
  993. SWITCH_TO(CommentEnd);
  994. }
  995. ANYTHING_ELSE
  996. {
  997. TODO();
  998. }
  999. }
  1000. END_STATE
  1001. BEGIN_STATE(CharacterReference)
  1002. {
  1003. }
  1004. END_STATE
  1005. BEGIN_STATE(RCDATA)
  1006. {
  1007. ON('&')
  1008. {
  1009. m_return_state = State::RCDATA;
  1010. SWITCH_TO(CharacterReference);
  1011. }
  1012. ON('<')
  1013. {
  1014. SWITCH_TO(RCDATALessThanSign);
  1015. }
  1016. ON(0)
  1017. {
  1018. TODO();
  1019. }
  1020. ON_EOF
  1021. {
  1022. EMIT_EOF;
  1023. }
  1024. ANYTHING_ELSE
  1025. {
  1026. EMIT_CURRENT_CHARACTER;
  1027. }
  1028. }
  1029. END_STATE
  1030. BEGIN_STATE(RCDATALessThanSign)
  1031. {
  1032. ON('/')
  1033. {
  1034. m_temporary_buffer.clear();
  1035. SWITCH_TO(RCDATAEndTagOpen);
  1036. }
  1037. ANYTHING_ELSE
  1038. {
  1039. EMIT_CHARACTER('<');
  1040. RECONSUME_IN(RCDATA);
  1041. }
  1042. }
  1043. END_STATE
  1044. BEGIN_STATE(RCDATAEndTagOpen)
  1045. {
  1046. ON_ASCII_ALPHA
  1047. {
  1048. create_new_token(HTMLToken::Type::EndTag);
  1049. RECONSUME_IN(RCDATAEndTagName);
  1050. }
  1051. ANYTHING_ELSE
  1052. {
  1053. // FIXME: Emit a U+003C LESS-THAN SIGN character token and a U+002F SOLIDUS character token. Reconsume in the RCDATA state.
  1054. TODO();
  1055. }
  1056. }
  1057. END_STATE
  1058. BEGIN_STATE(RCDATAEndTagName)
  1059. {
  1060. ON_WHITESPACE
  1061. {
  1062. TODO();
  1063. }
  1064. ON('/')
  1065. {
  1066. TODO();
  1067. }
  1068. ON('>')
  1069. {
  1070. if (!current_end_tag_token_is_appropriate()) {
  1071. // FIXME: Otherwise, treat it as per the "anything else" entry below.
  1072. TODO();
  1073. }
  1074. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  1075. }
  1076. ON_ASCII_UPPER_ALPHA
  1077. {
  1078. m_current_token.m_tag.tag_name.append(tolower(current_input_character.value()));
  1079. m_temporary_buffer.append(current_input_character.value());
  1080. continue;
  1081. }
  1082. ON_ASCII_LOWER_ALPHA
  1083. {
  1084. m_current_token.m_tag.tag_name.append(current_input_character.value());
  1085. m_temporary_buffer.append(current_input_character.value());
  1086. continue;
  1087. }
  1088. ANYTHING_ELSE
  1089. {
  1090. TODO();
  1091. }
  1092. }
  1093. END_STATE
  1094. BEGIN_STATE(RAWTEXT)
  1095. {
  1096. ON('<')
  1097. {
  1098. SWITCH_TO(RAWTEXTLessThanSign);
  1099. }
  1100. ON(0)
  1101. {
  1102. TODO();
  1103. }
  1104. ON_EOF
  1105. {
  1106. EMIT_EOF;
  1107. }
  1108. ANYTHING_ELSE
  1109. {
  1110. EMIT_CURRENT_CHARACTER;
  1111. }
  1112. }
  1113. END_STATE
  1114. BEGIN_STATE(RAWTEXTLessThanSign)
  1115. {
  1116. ON('/')
  1117. {
  1118. m_temporary_buffer.clear();
  1119. SWITCH_TO(RAWTEXTEndTagOpen);
  1120. }
  1121. ANYTHING_ELSE
  1122. {
  1123. EMIT_CHARACTER('<');
  1124. RECONSUME_IN(RAWTEXT);
  1125. }
  1126. }
  1127. END_STATE
  1128. BEGIN_STATE(RAWTEXTEndTagOpen)
  1129. {
  1130. ON_ASCII_ALPHA
  1131. {
  1132. create_new_token(HTMLToken::Type::EndTag);
  1133. RECONSUME_IN(RAWTEXTEndTagName);
  1134. }
  1135. ANYTHING_ELSE
  1136. {
  1137. // FIXME: Emit a U+003C LESS-THAN SIGN character token and a U+002F SOLIDUS character token. Reconsume in the RAWTEXT state.
  1138. TODO();
  1139. }
  1140. }
  1141. END_STATE
  1142. BEGIN_STATE(RAWTEXTEndTagName)
  1143. {
  1144. ON_WHITESPACE
  1145. {
  1146. TODO();
  1147. }
  1148. ON('/')
  1149. {
  1150. TODO();
  1151. }
  1152. ON('>')
  1153. {
  1154. if (!current_end_tag_token_is_appropriate()) {
  1155. // FIXME: Otherwise, treat it as per the "anything else" entry below.
  1156. TODO();
  1157. }
  1158. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  1159. }
  1160. ON_ASCII_UPPER_ALPHA
  1161. {
  1162. m_current_token.m_tag.tag_name.append(tolower(current_input_character.value()));
  1163. m_temporary_buffer.append(current_input_character.value());
  1164. continue;
  1165. }
  1166. ON_ASCII_LOWER_ALPHA
  1167. {
  1168. m_current_token.m_tag.tag_name.append(current_input_character.value());
  1169. m_temporary_buffer.append(current_input_character.value());
  1170. continue;
  1171. }
  1172. ANYTHING_ELSE
  1173. {
  1174. TODO();
  1175. }
  1176. }
  1177. END_STATE
  1178. BEGIN_STATE(ScriptData)
  1179. {
  1180. ON('<')
  1181. {
  1182. SWITCH_TO(ScriptDataLessThanSign);
  1183. }
  1184. ON(0)
  1185. {
  1186. TODO();
  1187. }
  1188. ON_EOF
  1189. {
  1190. EMIT_EOF;
  1191. }
  1192. ANYTHING_ELSE
  1193. {
  1194. EMIT_CURRENT_CHARACTER;
  1195. }
  1196. }
  1197. END_STATE
  1198. BEGIN_STATE(ScriptDataLessThanSign)
  1199. {
  1200. ON('/')
  1201. {
  1202. m_temporary_buffer.clear();
  1203. SWITCH_TO(ScriptDataEndTagOpen);
  1204. }
  1205. ON('!')
  1206. {
  1207. TODO();
  1208. }
  1209. ANYTHING_ELSE
  1210. {
  1211. EMIT_CHARACTER_AND_RECONSUME_IN('<', ScriptData);
  1212. }
  1213. }
  1214. END_STATE
  1215. BEGIN_STATE(ScriptDataEndTagOpen)
  1216. {
  1217. ON_ASCII_ALPHA
  1218. {
  1219. create_new_token(HTMLToken::Type::EndTag);
  1220. RECONSUME_IN(ScriptDataEndTagName);
  1221. }
  1222. ANYTHING_ELSE
  1223. {
  1224. TODO();
  1225. }
  1226. }
  1227. END_STATE
  1228. BEGIN_STATE(ScriptDataEndTagName)
  1229. {
  1230. ON_WHITESPACE
  1231. {
  1232. if (current_end_tag_token_is_appropriate())
  1233. SWITCH_TO(BeforeAttributeName);
  1234. // FIXME: Otherwise, treat it as per the "anything else" entry below.
  1235. TODO();
  1236. }
  1237. ON('/')
  1238. {
  1239. if (current_end_tag_token_is_appropriate())
  1240. SWITCH_TO(SelfClosingStartTag);
  1241. // FIXME: Otherwise, treat it as per the "anything else" entry below.
  1242. TODO();
  1243. }
  1244. ON('>')
  1245. {
  1246. if (current_end_tag_token_is_appropriate())
  1247. SWITCH_TO_AND_EMIT_CURRENT_TOKEN(Data);
  1248. // FIXME: Otherwise, treat it as per the "anything else" entry below.
  1249. TODO();
  1250. }
  1251. ON_ASCII_UPPER_ALPHA
  1252. {
  1253. m_current_token.m_tag.tag_name.append(tolower(current_input_character.value()));
  1254. m_temporary_buffer.append(current_input_character.value());
  1255. continue;
  1256. }
  1257. ON_ASCII_LOWER_ALPHA
  1258. {
  1259. m_current_token.m_tag.tag_name.append(current_input_character.value());
  1260. m_temporary_buffer.append(current_input_character.value());
  1261. continue;
  1262. }
  1263. ANYTHING_ELSE
  1264. {
  1265. TODO();
  1266. }
  1267. }
  1268. END_STATE
  1269. default:
  1270. TODO();
  1271. }
  1272. }
  1273. }
  1274. bool HTMLTokenizer::consume_next_if_match(const StringView& string, CaseSensitivity case_sensitivity)
  1275. {
  1276. for (size_t i = 0; i < string.length(); ++i) {
  1277. auto codepoint = peek_codepoint(i);
  1278. if (!codepoint.has_value())
  1279. return false;
  1280. // FIXME: This should be more Unicode-aware.
  1281. if (case_sensitivity == CaseSensitivity::CaseInsensitive) {
  1282. if (codepoint.value() < 0x80) {
  1283. if (tolower(codepoint.value()) != tolower(string[i]))
  1284. return false;
  1285. continue;
  1286. }
  1287. }
  1288. if (codepoint.value() != (u32)string[i])
  1289. return false;
  1290. }
  1291. m_cursor += string.length();
  1292. return true;
  1293. }
  1294. void HTMLTokenizer::create_new_token(HTMLToken::Type type)
  1295. {
  1296. m_current_token = {};
  1297. m_current_token.m_type = type;
  1298. }
  1299. HTMLTokenizer::HTMLTokenizer(const StringView& input)
  1300. : m_input(input)
  1301. {
  1302. }
  1303. void HTMLTokenizer::will_switch_to([[maybe_unused]] State new_state)
  1304. {
  1305. #ifdef TOKENIZER_TRACE
  1306. dbg() << "[" << state_name(m_state) << "] Switch to " << state_name(new_state);
  1307. #endif
  1308. }
  1309. void HTMLTokenizer::will_reconsume_in([[maybe_unused]] State new_state)
  1310. {
  1311. #ifdef TOKENIZER_TRACE
  1312. dbg() << "[" << state_name(m_state) << "] Reconsume in " << state_name(new_state);
  1313. #endif
  1314. }
  1315. void HTMLTokenizer::switch_to(Badge<HTMLDocumentParser>, State new_state)
  1316. {
  1317. #ifdef TOKENIZER_TRACE
  1318. dbg() << "[" << state_name(m_state) << "] Parser switches tokenizer state to " << state_name(new_state);
  1319. #endif
  1320. m_state = new_state;
  1321. }
  1322. void HTMLTokenizer::will_emit(HTMLToken& token)
  1323. {
  1324. if (token.is_start_tag())
  1325. m_last_emitted_start_tag = token;
  1326. }
  1327. bool HTMLTokenizer::current_end_tag_token_is_appropriate() const
  1328. {
  1329. ASSERT(m_current_token.is_end_tag());
  1330. if (!m_last_emitted_start_tag.is_start_tag())
  1331. return false;
  1332. return m_current_token.tag_name() == m_last_emitted_start_tag.tag_name();
  1333. }
  1334. }