123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926 |
- /*
- * Copyright (c) 2024, Andrew Kaster <andrew@ladybird.org>>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- import AK
- import Collections
- import Foundation
- @_exported import WebCxx
- extension Swift.String {
- public init?(decoding: AK.StringView, as: AK.StringView) {
- let maybe_decoded = Web.HTML.decode_to_utf8(decoding, `as`)
- if maybe_decoded.hasValue {
- self.init(akString: maybe_decoded.value!)
- } else {
- return nil
- }
- }
- public mutating func takeString() -> Swift.String {
- let result = self
- self = ""
- return result
- }
- }
- public class HTMLTokenizer {
- public enum State {
- case Data
- case RCDATA
- case RAWTEXT
- case ScriptData
- case PLAINTEXT
- case TagOpen
- case EndTagOpen
- case TagName
- case RCDATALessThanSign
- case RCDATAEndTagOpen
- case RCDATAEndTagName
- case RAWTEXTLessThanSign
- case RAWTEXTEndTagOpen
- case RAWTEXTEndTagName
- case ScriptDataLessThanSign
- case ScriptDataEndTagOpen
- case ScriptDataEndTagName
- case ScriptDataEscapeStart
- case ScriptDataEscapeStartDash
- case ScriptDataEscaped
- case ScriptDataEscapedDash
- case ScriptDataEscapedDashDash
- case ScriptDataEscapedLessThanSign
- case ScriptDataEscapedEndTagOpen
- case ScriptDataEscapedEndTagName
- case ScriptDataDoubleEscapeStart
- case ScriptDataDoubleEscaped
- case ScriptDataDoubleEscapedDash
- case ScriptDataDoubleEscapedDashDash
- case ScriptDataDoubleEscapedLessThanSign
- case ScriptDataDoubleEscapeEnd
- case BeforeAttributeName
- case AttributeName
- case AfterAttributeName
- case BeforeAttributeValue
- case AttributeValueDoubleQuoted
- case AttributeValueSingleQuoted
- case AttributeValueUnquoted
- case AfterAttributeValueQuoted
- case SelfClosingStartTag
- case BogusComment
- case MarkupDeclarationOpen
- case CommentStart
- case CommentStartDash
- case Comment
- case CommentLessThanSign
- case CommentLessThanSignBang
- case CommentLessThanSignBangDash
- case CommentLessThanSignBangDashDash
- case CommentEndDash
- case CommentEnd
- case CommentEndBang
- case DOCTYPE
- case BeforeDOCTYPEName
- case DOCTYPEName
- case AfterDOCTYPEName
- case AfterDOCTYPEPublicKeyword
- case BeforeDOCTYPEPublicIdentifier
- case DOCTYPEPublicIdentifierDoubleQuoted
- case DOCTYPEPublicIdentifierSingleQuoted
- case AfterDOCTYPEPublicIdentifier
- case BetweenDOCTYPEPublicAndSystemIdentifiers
- case AfterDOCTYPESystemKeyword
- case BeforeDOCTYPESystemIdentifier
- case DOCTYPESystemIdentifierDoubleQuoted
- case DOCTYPESystemIdentifierSingleQuoted
- case AfterDOCTYPESystemIdentifier
- case BogusDOCTYPE
- case CDATASection
- case CDATASectionBracket
- case CDATASectionEnd
- case CharacterReference
- case NamedCharacterReference
- case AmbiguousAmpersand
- case NumericCharacterReference
- case HexadecimalCharacterReferenceStart
- case DecimalCharacterReferenceStart
- case HexadecimalCharacterReference
- case DecimalCharacterReference
- case NumericCharacterReferenceEnd
- }
- private var input = Swift.String()
- private var cursor: Swift.String.Index
- private var previousCursor: Swift.String.Index
- public private(set) var state = State.Data
- private var returnState = State.Data
- private var currentToken = HTMLToken()
- private var queuedTokens = Deque<HTMLToken>()
- private var currentBuilder = Swift.String()
- private var temporaryBuffer = Swift.String()
- private var lastStartTagName: Swift.String? = nil
- private var currentTokensAttributes: [HTMLToken.Attribute]? = nil
- private var currentAttribute: HTMLToken.Attribute? = nil
- private var characterReferenceCode: Int = 0
- private var aborted = false
- private var hasEmittedEOF = false
- // https://infra.spec.whatwg.org/#noncharacter
- // A noncharacter is a code point that is in the range U+FDD0 to U+FDEF, inclusive,
- // or U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, U+2FFFE, U+2FFFF, U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF,
- // U+5FFFE, U+5FFFF, U+6FFFE, U+6FFFF, U+7FFFE, U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF,
- // U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF, U+DFFFE, U+DFFFF, U+EFFFE, U+EFFFF,
- // U+FFFFE, U+FFFFF, U+10FFFE, or U+10FFFF.
- static private var nonCharacter = CharacterSet(charactersIn: Unicode.Scalar(0xFDD0)!...Unicode.Scalar(0xFDEF)!)
- .union(CharacterSet(charactersIn: "\u{FFFE}"..."\u{FFFF}"))
- .union(CharacterSet(charactersIn: "\u{1FFFE}"..."\u{1FFFF}"))
- .union(CharacterSet(charactersIn: "\u{2FFFE}"..."\u{2FFFF}"))
- .union(CharacterSet(charactersIn: "\u{3FFFE}"..."\u{3FFFF}"))
- .union(CharacterSet(charactersIn: "\u{4FFFE}"..."\u{4FFFF}"))
- .union(CharacterSet(charactersIn: "\u{5FFFE}"..."\u{5FFFF}"))
- .union(CharacterSet(charactersIn: "\u{6FFFE}"..."\u{6FFFF}"))
- .union(CharacterSet(charactersIn: "\u{7FFFE}"..."\u{7FFFF}"))
- .union(CharacterSet(charactersIn: "\u{8FFFE}"..."\u{8FFFF}"))
- .union(CharacterSet(charactersIn: "\u{9FFFE}"..."\u{9FFFF}"))
- .union(CharacterSet(charactersIn: "\u{AFFFE}"..."\u{AFFFF}"))
- .union(CharacterSet(charactersIn: "\u{BFFFE}"..."\u{BFFFF}"))
- .union(CharacterSet(charactersIn: "\u{CFFFE}"..."\u{CFFFF}"))
- .union(CharacterSet(charactersIn: "\u{DFFFE}"..."\u{DFFFF}"))
- .union(CharacterSet(charactersIn: "\u{EFFFE}"..."\u{EFFFF}"))
- .union(CharacterSet(charactersIn: "\u{FFFFE}"..."\u{FFFFF}"))
- .union(CharacterSet(charactersIn: "\u{10FFFE}"..."\u{10FFFF}"))
- // https://infra.spec.whatwg.org/#ascii-whitespace
- static private var asciiWhitespace = CharacterSet(charactersIn: "\t\n\u{000C}\u{000D} ")
- // https://infra.spec.whatwg.org/#c0-control
- static private var c0Control = CharacterSet(charactersIn: "\u{0000}"..."\u{001F}")
- // https://infra.spec.whatwg.org/#control
- static private var control = c0Control.union(CharacterSet(charactersIn: "\u{007F}"..."\u{009F}"))
- // IMPLEMENTATION DEFINED: Used for the numeric character reference end state
- static private var controlNotAsciiWhitespace = control.subtracting(asciiWhitespace)
- // https://infra.spec.whatwg.org/#ascii-digit
- static private var asciiDigit = CharacterSet(charactersIn: "0123456789")
- // https://infra.spec.whatwg.org/#ascii-upper-hex-digit
- static private var asciiUpperHexDigit = CharacterSet(charactersIn: "ABCDEF")
- // https://infra.spec.whatwg.org/#ascii-lower-hex-digit
- static private var asciiLowerHexDigit = CharacterSet(charactersIn: "abcdef")
- // https://infra.spec.whatwg.org/#ascii-hex-digit
- static private var asciiHexDigit = asciiUpperHexDigit.union(asciiLowerHexDigit)
- // https://infra.spec.whatwg.org/#ascii-upper-alpha
- static private var asciiUpperAlpha = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
- // https://infra.spec.whatwg.org/#ascii-lower-alpha
- static private var asciiLowerAlpha = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyz")
- // https://infra.spec.whatwg.org/#ascii-upper-alpha
- static private var asciiAlpha = asciiUpperAlpha.union(asciiLowerAlpha)
- // https://infra.spec.whatwg.org/#ascii-alphanumeric
- static private var asciiAlphanumeric = asciiAlpha.union(asciiDigit)
- static private var characterReferenceControlCodeMapping: [Int: Unicode.Scalar] =
- [
- 0x80: Unicode.Scalar(0x20AC)!, // €
- 0x82: Unicode.Scalar(0x201A)!, // ‚
- 0x83: Unicode.Scalar(0x0192)!, // ƒ
- 0x84: Unicode.Scalar(0x201E)!, // „
- 0x85: Unicode.Scalar(0x2026)!, // …
- 0x86: Unicode.Scalar(0x2020)!, // †
- 0x87: Unicode.Scalar(0x2021)!, // ‡
- 0x88: Unicode.Scalar(0x02C6)!, // ˆ
- 0x89: Unicode.Scalar(0x2030)!, // ‰
- 0x8A: Unicode.Scalar(0x0160)!, // Š
- 0x8B: Unicode.Scalar(0x2039)!, // ‹
- 0x8C: Unicode.Scalar(0x0152)!, // Œ
- 0x8E: Unicode.Scalar(0x017D)!, // Ž
- 0x91: Unicode.Scalar(0x2018)!, // ‘
- 0x92: Unicode.Scalar(0x2019)!, // ’
- 0x93: Unicode.Scalar(0x201C)!, // “
- 0x94: Unicode.Scalar(0x201D)!, // ”
- 0x95: Unicode.Scalar(0x2022)!, // •
- 0x96: Unicode.Scalar(0x2013)!, // –
- 0x97: Unicode.Scalar(0x2014)!, // —
- 0x98: Unicode.Scalar(0x02DC)!, // ˜
- 0x99: Unicode.Scalar(0x2122)!, // ™
- 0x9A: Unicode.Scalar(0x0161)!, // š
- 0x9B: Unicode.Scalar(0x203A)!, // ›
- 0x9C: Unicode.Scalar(0x0153)!, // œ
- 0x9E: Unicode.Scalar(0x017E)!, // ž
- 0x9F: Unicode.Scalar(0x0178)!, // Ÿ
- ]
- public init() {
- self.cursor = self.input.startIndex
- self.previousCursor = self.input.startIndex
- }
- public init?(input: AK.StringView, encoding: AK.StringView) {
- if let string = Swift.String(decoding: input, as: encoding) {
- self.input = string
- } else {
- return nil
- }
- self.cursor = self.input.startIndex
- self.previousCursor = self.input.startIndex
- }
- public convenience init?(input: AK.StringView) {
- self.init(input: input, encoding: "UTF-8")
- }
- public func abort() {
- self.aborted = true
- }
- func skip(_ count: Int) {
- self.previousCursor = self.cursor
- self.cursor = self.input.index(self.cursor, offsetBy: count, limitedBy: self.input.endIndex) ?? input.endIndex
- }
- func peekCodePoint(_ offset: Int = 0) -> Character? {
- guard let index = self.input.index(self.cursor, offsetBy: offset, limitedBy: self.input.index(before: self.input.endIndex)) else {
- return nil
- }
- return self.input[index]
- }
- func peekNext(count: Int) -> Swift.Substring? {
- guard let endIndex = self.input.index(self.cursor, offsetBy: count, limitedBy: self.input.index(before: self.input.endIndex)) else {
- return nil
- }
- return self.input[self.cursor..<endIndex]
- }
- func nextCodePoint() -> Character? {
- guard self.cursor < self.input.endIndex else {
- return nil
- }
- // https://html.spec.whatwg.org/multipage/parsing.html#preprocessing-the-input-stream:tokenization
- // https://infra.spec.whatwg.org/#normalize-newlines
- var codePoint: Character
- if let peeked = peekCodePoint(), let peekedNext = peekCodePoint(1), peeked == "\r", peekedNext == "\n" {
- // replace every U+000D CR U+000A LF code point pair with a single U+000A LF code point,
- skip(2)
- codePoint = "\n"
- } else if let peeked = peekCodePoint(), peeked == "\r" {
- // replace every remaining U+000D CR code point with a U+000A LF code point.
- skip(1)
- codePoint = "\n"
- } else {
- skip(1)
- codePoint = self.input[self.previousCursor]
- }
- return codePoint
- }
- func restoreCursorToPrevious() {
- self.cursor = self.previousCursor
- }
- func createNewToken(_ token: HTMLToken) {
- self.currentToken = token
- if self.currentToken.isTag() {
- self.currentTokensAttributes = []
- }
- // FIXME: Assign Position
- }
- enum AttributeStringBehavior {
- case SetName
- case SetValue
- case IgnoreString
- }
- func finalizeCurrentAttribute(_ behavior: AttributeStringBehavior) {
- precondition(self.currentAttribute != nil && self.currentTokensAttributes != nil)
- switch behavior {
- case .SetName:
- self.currentAttribute!.localName = self.currentBuilder.takeString()
- case .SetValue:
- self.currentAttribute!.value = self.currentBuilder.takeString()
- case .IgnoreString:
- _ = self.currentBuilder.takeString()
- }
- self.currentTokensAttributes!.append(self.currentAttribute!)
- self.currentAttribute = nil
- }
- enum NextTokenState {
- case Emit(token: HTMLToken?)
- case SwitchTo
- case Reconsume(inputCharacter: Character?)
- case ReprocessQueue
- case Continue
- }
- public func nextToken(stopAtInsertionPoint: Bool = false) -> HTMLToken? {
- let processQueue = { () -> HTMLToken?? in
- if let token = self.queuedTokens.popFirst() {
- return token
- }
- return self.aborted ? Optional(nil) : nil
- }
- if let maybeToken = processQueue() {
- return maybeToken
- }
- var nextInputCharacter: Character? = nil
- while true {
- // FIXME: Handle insertion point
- switch nextTokenImpl(nextInputCharacter) {
- case .Emit(let token):
- return token
- case .SwitchTo, .Continue:
- nextInputCharacter = nil
- break
- case .Reconsume(let character):
- nextInputCharacter = character
- break
- case .ReprocessQueue:
- if let maybeToken = processQueue() {
- return maybeToken
- }
- nextInputCharacter = nil
- break
- }
- }
- }
- func continueInCurrentState() -> NextTokenState {
- return .Continue
- }
- func switchTo(_ state: State) -> NextTokenState {
- self.state = state
- return .SwitchTo
- }
- func reconsume(_ character: Character?, `in` state: State) -> NextTokenState {
- self.state = state
- return .Reconsume(inputCharacter: character)
- }
- func switchToReturnState() -> NextTokenState {
- self.state = self.returnState
- return .ReprocessQueue
- }
- func reconsumeInReturnState(_ character: Character?) -> NextTokenState {
- self.state = self.returnState
- if character != nil {
- restoreCursorToPrevious()
- }
- return .ReprocessQueue
- }
- func switchToAndEmitCurrentToken(_ state: State) -> NextTokenState {
- self.state = state
- if self.currentToken.isTag() {
- self.currentToken.attributes = self.currentTokensAttributes ?? []
- self.currentTokensAttributes = nil
- }
- self.queuedTokens.append(self.currentToken)
- self.currentToken = HTMLToken()
- return .Emit(token: self.queuedTokens.popFirst()!)
- }
- func switchToAndEmitCharacter(_ state: State, character: Character) -> NextTokenState {
- self.state = state
- return emitCharacter(character)
- }
- func emitCharacterAndReconsume(_ character: Character, `in`: State, currentInputCharacter: Character?) -> NextTokenState {
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: character)))
- self.state = `in`
- return .Reconsume(inputCharacter: currentInputCharacter)
- }
- func emitEOF() -> NextTokenState {
- if self.hasEmittedEOF {
- return .Emit(token: nil)
- }
- self.hasEmittedEOF = true
- createNewToken(HTMLToken(type: .EndOfFile))
- self.queuedTokens.append(self.currentToken)
- self.currentToken = HTMLToken()
- return .Emit(token: self.queuedTokens.popFirst()!)
- }
- func emitCurrentTokenFollowedByEOF() -> NextTokenState {
- precondition(!self.hasEmittedEOF)
- if self.currentToken.isTag() {
- self.currentToken.attributes = self.currentTokensAttributes ?? []
- self.currentTokensAttributes = nil
- }
- self.queuedTokens.append(self.currentToken)
- self.currentToken = HTMLToken()
- return emitEOF()
- }
- func emitCharacter(_ character: Character) -> NextTokenState {
- createNewToken(HTMLToken(type: .Character(codePoint: character)))
- self.queuedTokens.append(self.currentToken)
- self.currentToken = HTMLToken()
- return .Emit(token: self.queuedTokens.popFirst()!)
- }
- func flushCodepointsConsumedAsACharacterReference() {
- if consumedAsPartOfAnAttribute() {
- self.currentBuilder += self.temporaryBuffer.takeString()
- } else {
- for codePoint in self.temporaryBuffer.takeString() {
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: codePoint)))
- }
- }
- }
- func consumedAsPartOfAnAttribute() -> Bool {
- return self.returnState == .AttributeValueDoubleQuoted || self.returnState == .AttributeValueSingleQuoted || self.returnState == .AttributeValueUnquoted
- }
- func isAppropriateEndTagToken(_ token: HTMLToken) -> Bool {
- guard case let .EndTag(endTagName, _, _, _) = token.type else {
- preconditionFailure("isAppropriateEndTagToken called with non-end-tag token")
- }
- if let startTagName = self.lastStartTagName {
- return startTagName == endTagName
- } else {
- return false
- }
- }
- func nextTokenImpl(_ nextInputCharacter: Character? = nil) -> NextTokenState {
- let dontConsumeNextInputCharacter = {
- self.restoreCursorToPrevious()
- }
- let _ = dontConsumeNextInputCharacter
- // Handle reconsume by passing the character around in the state enum
- let currentInputCharacter = nextInputCharacter ?? nextCodePoint()
- switch self.state {
- // 13.2.5.1 Data state, https://html.spec.whatwg.org/multipage/parsing.html#data-state
- case .Data:
- precondition(currentTokensAttributes == nil)
- switch currentInputCharacter {
- case "&":
- self.returnState = .Data
- return switchTo(.CharacterReference)
- case "<":
- return switchTo(.TagOpen)
- case "\0":
- // FIXME: log_parse_error()
- return emitCharacter("\u{FFFD}")
- case nil:
- return emitEOF()
- default:
- return emitCharacter(currentInputCharacter!)
- }
- // 13.2.5.2 RCDATA state, https://html.spec.whatwg.org/multipage/parsing.html#rcdata-state
- case .RCDATA:
- switch currentInputCharacter {
- case "&":
- self.returnState = .RCDATA
- return switchTo(.CharacterReference)
- case "<":
- return switchTo(.RCDATALessThanSign)
- case "\0":
- // FIXME: log_parse_error()
- return emitCharacter("\u{FFFD}")
- case nil:
- return emitEOF()
- default:
- return emitCharacter(currentInputCharacter!)
- }
- // 13.2.5.3. RAWTEXT state, https://html.spec.whatwg.org/multipage/parsing.html#rawtext-state
- case .RAWTEXT:
- switch currentInputCharacter {
- case "<":
- return switchTo(.RAWTEXTLessThanSign)
- case "\0":
- // FIXME: log_parse_error()
- return emitCharacter("\u{FFFD}")
- case nil:
- return emitEOF()
- default:
- return emitCharacter(currentInputCharacter!)
- }
- // 13.2.5.4 Script data state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-state
- case .ScriptData:
- switch currentInputCharacter {
- case "<":
- return switchTo(.ScriptDataLessThanSign)
- case "\0":
- // FIXME: log_parse_error()
- return emitCharacter("\u{FFFD}")
- case nil:
- return emitEOF()
- default:
- return emitCharacter(currentInputCharacter!)
- }
- // 13.2.5.5 PLAINTEXT state, https://html.spec.whatwg.org/multipage/parsing.html#plaintext-state
- case .PLAINTEXT:
- switch currentInputCharacter {
- case "\0":
- // FIXME: log_parse_error()
- return emitCharacter("\u{FFFD}")
- case nil:
- return emitEOF()
- default:
- return emitCharacter(currentInputCharacter!)
- }
- // 13.2.5.6 Tag open state https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state
- case .TagOpen:
- switch currentInputCharacter {
- case "!":
- return switchTo(.MarkupDeclarationOpen)
- case "/":
- return switchTo(.EndTagOpen)
- case let c? where HTMLTokenizer.asciiAlpha.contains(c.unicodeScalars.first!):
- createNewToken(HTMLToken(type: .StartTag(tagName: "")))
- return reconsume(currentInputCharacter!, in: .TagName)
- case "?":
- // FIXME: log_parse_error()
- createNewToken(HTMLToken(type: .Comment(data: "")))
- return reconsume(currentInputCharacter!, in: .BogusComment)
- case nil:
- // FIXME: log_parse_error()
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- return emitEOF()
- default:
- // FIXME: log_parse_error()
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- return reconsume(currentInputCharacter!, in: .Data)
- }
- // 13.2.5.7 End tag open state, https://html.spec.whatwg.org/multipage/parsing.html#end-tag-open-state
- case .EndTagOpen:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiAlpha.contains(c.unicodeScalars.first!):
- createNewToken(HTMLToken(type: .EndTag(tagName: "")))
- return reconsume(currentInputCharacter!, in: .TagName)
- default:
- return emitEOF()
- }
- // 13.2.5.8 Tag name state, https://html.spec.whatwg.org/multipage/parsing.html#tag-name-state
- case .TagName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- self.currentToken.tagName = self.currentBuilder.takeString()
- return switchTo(.BeforeAttributeName)
- case "/":
- self.currentToken.tagName = self.currentBuilder.takeString()
- return switchTo(.SelfClosingStartTag)
- case ">":
- self.currentToken.tagName = self.currentBuilder.takeString()
- return switchToAndEmitCurrentToken(.Data)
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- currentBuilder.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- return continueInCurrentState()
- case "\0":
- // FIXME: log_parse_error()
- currentBuilder += "\u{FFFD}"
- return continueInCurrentState()
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.9 RCDATA less-than sign state, https://html.spec.whatwg.org/multipage/parsing.html#rcdata-less-than-sign-state
- case .RCDATALessThanSign:
- switch currentInputCharacter {
- case "/":
- self.temporaryBuffer = ""
- return switchTo(.RCDATAEndTagOpen)
- default:
- return emitCharacterAndReconsume("<", in: .RCDATA, currentInputCharacter: currentInputCharacter)
- }
- // 13.2.5.10 RCDATA end tag open state, https://html.spec.whatwg.org/multipage/parsing.html#rcdata-end-tag-open-state
- case .RCDATAEndTagOpen:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiAlpha.contains(c.unicodeScalars.first!):
- createNewToken(HTMLToken(type: .EndTag(tagName: "")))
- return reconsume(currentInputCharacter!, in: .RCDATAEndTagName)
- default:
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "/")))
- return reconsume(currentInputCharacter, in: .RCDATA)
- }
- // 13.2.5.11 RCDATA end tag name state, https://html.spec.whatwg.org/multipage/parsing.html#rcdata-end-tag-name-state
- case .RCDATAEndTagName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchTo(.BeforeAttributeName)
- }
- break
- case "/":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchTo(.SelfClosingStartTag)
- }
- break
- case ">":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchToAndEmitCurrentToken(.Data)
- }
- break
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- self.temporaryBuffer.append(c)
- return continueInCurrentState()
- case let c? where HTMLTokenizer.asciiLowerAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(c)
- self.temporaryBuffer.append(c)
- return continueInCurrentState()
- default:
- break
- }
- // First three steps fall through to the "anything else" block
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "/")))
- // NOTE: The spec doesn't mention this, but it seems that m_current_token (an end tag) is just dropped in this case.
- self.currentBuilder = ""
- for codePoint in self.temporaryBuffer {
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: codePoint)))
- }
- return reconsume(currentInputCharacter, in: .RCDATA)
- // 13.2.5.12 RAWTEXT less-than sign state, https://html.spec.whatwg.org/multipage/parsing.html#rawtext-less-than-sign-state
- case .RAWTEXTLessThanSign:
- switch currentInputCharacter {
- case "/":
- self.temporaryBuffer = ""
- return switchTo(.RAWTEXTEndTagOpen)
- default:
- return emitCharacterAndReconsume("<", in: .RAWTEXT, currentInputCharacter: currentInputCharacter)
- }
- // 13.2.5.13 RAWTEXT end tag open state, https://html.spec.whatwg.org/multipage/parsing.html#rawtext-end-tag-open-state
- case .RAWTEXTEndTagOpen:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiAlpha.contains(c.unicodeScalars.first!):
- createNewToken(HTMLToken(type: .EndTag(tagName: "")))
- return reconsume(currentInputCharacter!, in: .RAWTEXTEndTagName)
- default:
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "/")))
- return reconsume(currentInputCharacter, in: .RAWTEXT)
- }
- // 13.2.5.14 RAWTEXT end tag name state, https://html.spec.whatwg.org/multipage/parsing.html#rawtext-end-tag-name-state
- case .RAWTEXTEndTagName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchTo(.BeforeAttributeName)
- }
- break
- case "/":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchTo(.SelfClosingStartTag)
- }
- break
- case ">":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchToAndEmitCurrentToken(.Data)
- }
- break
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- self.temporaryBuffer.append(c)
- return continueInCurrentState()
- case let c? where HTMLTokenizer.asciiLowerAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(c)
- self.temporaryBuffer.append(c)
- return continueInCurrentState()
- default:
- break
- }
- // First three steps fall through to the "anything else" block
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "/")))
- // NOTE: The spec doesn't mention this, but it seems that m_current_token (an end tag) is just dropped in this case.
- self.currentBuilder = ""
- for codePoint in self.temporaryBuffer {
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: codePoint)))
- }
- return reconsume(currentInputCharacter, in: .RAWTEXT)
- // 13.2.5.15 Script data less-than sign state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-less-than-sign-state
- case .ScriptDataLessThanSign:
- switch currentInputCharacter {
- case "/":
- self.temporaryBuffer = ""
- return switchTo(.ScriptDataEndTagOpen)
- case "!":
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "!")))
- return switchTo(.ScriptDataEscapeStart)
- default:
- return emitCharacterAndReconsume("<", in: .ScriptData, currentInputCharacter: currentInputCharacter)
- }
- // 13.2.5.16 Script data end tag open state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-end-tag-open-state
- case .ScriptDataEndTagOpen:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiAlpha.contains(c.unicodeScalars.first!):
- createNewToken(HTMLToken(type: .EndTag(tagName: "")))
- return reconsume(currentInputCharacter!, in: .ScriptDataEndTagName)
- default:
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "/")))
- return reconsume(currentInputCharacter, in: .ScriptData)
- }
- // 13.2.5.17 Script data end tag name state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-end-tag-name-state
- case .ScriptDataEndTagName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchTo(.BeforeAttributeName)
- }
- break
- case "/":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchTo(.SelfClosingStartTag)
- }
- break
- case ">":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchToAndEmitCurrentToken(.Data)
- }
- break
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- self.temporaryBuffer.append(c)
- return continueInCurrentState()
- case let c? where HTMLTokenizer.asciiLowerAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(c)
- self.temporaryBuffer.append(c)
- return continueInCurrentState()
- default:
- break
- }
- // First three steps fall through to the "anything else" block
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "/")))
- // NOTE: The spec doesn't mention this, but it seems that m_current_token (an end tag) is just dropped in this case.
- self.currentBuilder = ""
- for codePoint in self.temporaryBuffer {
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: codePoint)))
- }
- return reconsume(currentInputCharacter, in: .ScriptData)
- // 13.2.5.18 Script data escape start state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-escape-start-state
- case .ScriptDataEscapeStart:
- switch currentInputCharacter {
- case "-":
- return switchToAndEmitCharacter(.ScriptDataEscapeStartDash, character: "-")
- default:
- return reconsume(currentInputCharacter, in: .ScriptData)
- }
- // 13.2.5.19 Script data escape start dash state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-escape-start-dash-state
- case .ScriptDataEscapeStartDash:
- switch currentInputCharacter {
- case "-":
- return switchToAndEmitCharacter(.ScriptDataEscapedDashDash, character: "-")
- default:
- return reconsume(currentInputCharacter, in: .ScriptData)
- }
- // 13.2.5.20 Script data escaped state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-escaped-state
- case .ScriptDataEscaped:
- switch currentInputCharacter {
- case "-":
- return switchToAndEmitCharacter(.ScriptDataEscapedDash, character: "-")
- case "<":
- return switchTo(.ScriptDataEscapedLessThanSign)
- case "\0":
- // FIXME: log_parse_error()
- return emitCharacter("\u{FFFD}")
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- return emitCharacter(currentInputCharacter!)
- }
- // 13.2.5.21 Script data escaped dash state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-escaped-dash-state
- case .ScriptDataEscapedDash:
- switch currentInputCharacter {
- case "-":
- return switchToAndEmitCharacter(.ScriptDataEscapedDashDash, character: "-")
- case "<":
- return switchTo(.ScriptDataEscapedLessThanSign)
- case "\0":
- // FIXME: log_parse_error()
- return switchToAndEmitCharacter(.ScriptDataEscaped, character: "\u{FFFD}")
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- return switchToAndEmitCharacter(.ScriptDataEscaped, character: currentInputCharacter!)
- }
- // 13.2.5.22 Script data escaped dash dash state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-escaped-dash-dash-state
- case .ScriptDataEscapedDashDash:
- switch currentInputCharacter {
- case "-":
- return emitCharacter("-")
- case "<":
- return switchTo(.ScriptDataEscapedLessThanSign)
- case ">":
- return switchToAndEmitCharacter(.ScriptData, character: ">")
- case "\0":
- // FIXME: log_parse_error()
- return switchToAndEmitCharacter(.ScriptDataEscaped, character: "\u{FFFD}")
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- return switchToAndEmitCharacter(.ScriptDataEscaped, character: currentInputCharacter!)
- }
- // 13.2.5.23 Script data escaped less-than sign state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-escaped-less-than-sign-state
- case .ScriptDataEscapedLessThanSign:
- switch currentInputCharacter {
- case "/":
- self.temporaryBuffer = ""
- return switchTo(.ScriptDataEscapedEndTagOpen)
- case let c? where HTMLTokenizer.asciiAlpha.contains(c.unicodeScalars.first!):
- self.temporaryBuffer = ""
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- return reconsume(currentInputCharacter!, in: .ScriptDataDoubleEscapeStart)
- default:
- return emitCharacterAndReconsume("<", in: .ScriptDataEscaped, currentInputCharacter: currentInputCharacter)
- }
- // 13.2.5.24 Script data escaped end tag open state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-escaped-end-tag-open-state
- case .ScriptDataEscapedEndTagOpen:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiAlpha.contains(c.unicodeScalars.first!):
- createNewToken(HTMLToken(type: .EndTag(tagName: "")))
- return reconsume(currentInputCharacter!, in: .ScriptDataEscapedEndTagName)
- default:
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "/")))
- return reconsume(currentInputCharacter, in: .ScriptDataEscaped)
- }
- // 13.2.5.25 Script data escaped end tag name state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-escaped-end-tag-name-state
- case .ScriptDataEscapedEndTagName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchTo(.BeforeAttributeName)
- }
- break
- case "/":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchTo(.SelfClosingStartTag)
- }
- break
- case ">":
- if self.isAppropriateEndTagToken(currentToken) {
- return switchToAndEmitCurrentToken(.Data)
- }
- break
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- self.temporaryBuffer.append(c)
- return continueInCurrentState()
- case let c? where HTMLTokenizer.asciiLowerAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(c)
- self.temporaryBuffer.append(c)
- return continueInCurrentState()
- default:
- break
- }
- // First three steps fall through to the "anything else" block
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "<")))
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: "/")))
- // NOTE: The spec doesn't mention this, but it seems that m_current_token (an end tag) is just dropped in this case.
- self.currentBuilder = ""
- for codePoint in self.temporaryBuffer {
- self.queuedTokens.append(HTMLToken(type: .Character(codePoint: codePoint)))
- }
- return reconsume(currentInputCharacter, in: .ScriptDataEscaped)
- // 13.2.5.26 Script data double escape start state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escape-start-state
- case .ScriptDataDoubleEscapeStart:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ", "/", ">":
- if self.temporaryBuffer == "script" {
- return switchToAndEmitCharacter(.ScriptDataDoubleEscaped, character: currentInputCharacter!)
- } else {
- return switchToAndEmitCharacter(.ScriptDataEscaped, character: currentInputCharacter!)
- }
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- self.temporaryBuffer.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- return emitCharacter(currentInputCharacter!)
- case let c? where HTMLTokenizer.asciiLowerAlpha.contains(c.unicodeScalars.first!):
- self.temporaryBuffer.append(c)
- return emitCharacter(currentInputCharacter!)
- default:
- return reconsume(currentInputCharacter, in: .ScriptDataEscaped)
- }
- // 13.2.5.27 Script data double escaped state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escaped-state
- case .ScriptDataDoubleEscaped:
- switch currentInputCharacter {
- case "-":
- return switchToAndEmitCharacter(.ScriptDataDoubleEscapedDash, character: "-")
- case "<":
- return switchTo(.ScriptDataDoubleEscapedLessThanSign)
- case "\0":
- // FIXME: log_parse_error()
- return emitCharacter("\u{FFFD}")
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- return emitCharacter(currentInputCharacter!)
- }
- // 13.2.5.28 Script data double escaped dash state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escaped-dash-state
- case .ScriptDataDoubleEscapedDash:
- switch currentInputCharacter {
- case "-":
- return switchToAndEmitCharacter(.ScriptDataDoubleEscapedDashDash, character: "-")
- case "<":
- return switchTo(.ScriptDataDoubleEscapedLessThanSign)
- case "\0":
- // FIXME: log_parse_error()
- return switchToAndEmitCharacter(.ScriptDataDoubleEscaped, character: "\u{FFFD}")
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- return switchToAndEmitCharacter(.ScriptDataDoubleEscaped, character: currentInputCharacter!)
- }
- // 13.2.5.29 Script data double escaped dash dash state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escaped-dash-dash-state
- case .ScriptDataDoubleEscapedDashDash:
- switch currentInputCharacter {
- case "-":
- return emitCharacter("-")
- case "<":
- return switchToAndEmitCharacter(.ScriptDataDoubleEscapedLessThanSign, character: "<")
- case ">":
- return switchToAndEmitCharacter(.ScriptData, character: ">")
- case "\0":
- // FIXME: log_parse_error()
- return switchToAndEmitCharacter(.ScriptDataDoubleEscaped, character: "\u{FFFD}")
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- return switchToAndEmitCharacter(.ScriptDataDoubleEscaped, character: currentInputCharacter!)
- }
- // 13.2.5.30 Script data double escaped less-than sign state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escaped-less-than-sign-state
- case .ScriptDataDoubleEscapedLessThanSign:
- switch currentInputCharacter {
- case "/":
- self.temporaryBuffer = ""
- return switchToAndEmitCharacter(.ScriptDataDoubleEscapeEnd, character: "/")
- default:
- return reconsume(currentInputCharacter, in: .ScriptDataDoubleEscaped)
- }
- // 13.2.5.31 Script data double escape end state, https://html.spec.whatwg.org/multipage/parsing.html#script-data-double-escape-end-state
- case .ScriptDataDoubleEscapeEnd:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ", "/", ">":
- if self.temporaryBuffer == "script" {
- return switchToAndEmitCharacter(.ScriptDataEscaped, character: currentInputCharacter!)
- } else {
- return switchToAndEmitCharacter(.ScriptDataDoubleEscaped, character: currentInputCharacter!)
- }
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- self.temporaryBuffer.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- return emitCharacter(currentInputCharacter!)
- case let c? where HTMLTokenizer.asciiLowerAlpha.contains(c.unicodeScalars.first!):
- self.temporaryBuffer.append(c)
- return emitCharacter(currentInputCharacter!)
- default:
- return reconsume(currentInputCharacter, in: .ScriptDataDoubleEscaped)
- }
- // 13.2.5.32 Before attribute name state, https://html.spec.whatwg.org/multipage/parsing.html#before-attribute-name-state
- case .BeforeAttributeName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case "/", ">", nil:
- return reconsume(currentInputCharacter, in: .AfterAttributeName)
- case "=":
- // FIXME: log_parse_error()
- self.currentBuilder = Swift.String(currentInputCharacter!)
- self.currentAttribute = HTMLToken.Attribute(localName: "", value: "")
- return switchTo(.AttributeName)
- default:
- self.currentAttribute = HTMLToken.Attribute(localName: "", value: "")
- return reconsume(currentInputCharacter!, in: .AttributeName)
- }
- // 13.2.5.33 Attribute name state, https://html.spec.whatwg.org/multipage/parsing.html#attribute-name-state
- case .AttributeName:
- // FIXME: When the user agent leaves the attribute name state (and before emitting the tag token, if appropriate),
- // the complete attribute's name must be compared to the other attributes on the same token;
- // if there is already an attribute on the token with the exact same name, then this is a duplicate-attribute
- // parse error and the new attribute must be removed from the token.
- // NOTE: If an attribute is so removed from a token, it, and the value that gets associated with it, if any,
- // are never subsequently used by the parser, and are therefore effectively discarded. Removing the attribute
- // in this way does not change its status as the "current attribute" for the purposes of the tokenizer, however.
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ", "/", ">", nil:
- // FIXME: set name position
- self.currentAttribute!.localName = self.currentBuilder.takeString()
- return reconsume(currentInputCharacter, in: .AfterAttributeName)
- case "=":
- // FIXME: set name position
- self.currentAttribute!.localName = self.currentBuilder.takeString()
- return switchTo(.BeforeAttributeValue)
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- return continueInCurrentState()
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.34 After attribute name state, https://html.spec.whatwg.org/multipage/parsing.html#after-attribute-name-state
- case .AfterAttributeName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case "/":
- self.finalizeCurrentAttribute(.SetName)
- return switchTo(.SelfClosingStartTag)
- case "=":
- self.finalizeCurrentAttribute(.SetName)
- return switchTo(.BeforeAttributeValue)
- case ">":
- self.finalizeCurrentAttribute(.SetName)
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.finalizeCurrentAttribute(.IgnoreString)
- return emitEOF()
- default:
- self.finalizeCurrentAttribute(.SetName)
- self.currentAttribute = HTMLToken.Attribute(localName: "", value: "")
- return reconsume(currentInputCharacter!, in: .AttributeName)
- }
- // 13.2.5.35 Before attribute value state, https://html.spec.whatwg.org/multipage/parsing.html#before-attribute-value-state
- case .BeforeAttributeValue:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case "\"":
- return switchTo(.AttributeValueDoubleQuoted)
- case "'":
- return switchTo(.AttributeValueSingleQuoted)
- case ">":
- // FIXME: log_parse_error()
- self.finalizeCurrentAttribute(.IgnoreString)
- return switchToAndEmitCurrentToken(.Data)
- default:
- return reconsume(currentInputCharacter, in: .AttributeValueUnquoted)
- }
- // 13.2.5.36 Attribute value (double-quoted) state, https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-double-quoted-state
- case .AttributeValueDoubleQuoted:
- switch currentInputCharacter {
- case "\"":
- return switchTo(.AfterAttributeValueQuoted)
- case "&":
- self.returnState = .AttributeValueDoubleQuoted
- return switchTo(.CharacterReference)
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case nil:
- // FIXME: log_parse_error()
- self.finalizeCurrentAttribute(.IgnoreString)
- return emitEOF()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.37 Attribute value (single-quoted) state, https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-single-quoted-state
- case .AttributeValueSingleQuoted:
- switch currentInputCharacter {
- case "'":
- return switchTo(.AfterAttributeValueQuoted)
- case "&":
- self.returnState = .AttributeValueSingleQuoted
- return switchTo(.CharacterReference)
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.38 Attribute value (unquoted) state, https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-unquoted-state
- case .AttributeValueUnquoted:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- self.finalizeCurrentAttribute(.SetValue)
- return switchTo(.BeforeAttributeName)
- case "&":
- self.returnState = .AttributeValueUnquoted
- return switchTo(.CharacterReference)
- case ">":
- self.finalizeCurrentAttribute(.SetValue)
- return switchToAndEmitCurrentToken(.Data)
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case "\"", "'", "<", "=", "`":
- // FIXME: log_parse_error()
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- case nil:
- // FIXME: log_parse_error()
- self.finalizeCurrentAttribute(.IgnoreString)
- return emitEOF()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.39 After attribute value (quoted) state, https://html.spec.whatwg.org/multipage/parsing.html#after-attribute-value-quoted-state
- case .AfterAttributeValueQuoted:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- self.finalizeCurrentAttribute(.SetValue)
- return switchTo(.BeforeAttributeName)
- case "/":
- self.finalizeCurrentAttribute(.SetValue)
- return switchTo(.SelfClosingStartTag)
- case ">":
- self.finalizeCurrentAttribute(.SetValue)
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.finalizeCurrentAttribute(.IgnoreString)
- return emitEOF()
- default:
- // FIXME: log_parse_error()
- self.finalizeCurrentAttribute(.SetValue)
- return reconsume(currentInputCharacter!, in: .BeforeAttributeName)
- }
- // 13.2.5.40 Self-closing start tag state, https://html.spec.whatwg.org/multipage/parsing.html#self-closing-start-tag-state
- case .SelfClosingStartTag:
- switch currentInputCharacter {
- case ">":
- self.currentToken.selfClosing = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- // FIXME: log_parse_error()
- return reconsume(currentInputCharacter!, in: .BeforeAttributeName)
- }
- // 13.2.5.41 Bogus comment state, https://html.spec.whatwg.org/multipage/parsing.html#bogus-comment-state
- case .BogusComment:
- switch currentInputCharacter {
- case ">":
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return emitCurrentTokenFollowedByEOF()
- case "\0":
- // FIXME: log_parse_error()
- currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.42 Markup declaration open state, https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state
- case .MarkupDeclarationOpen:
- dontConsumeNextInputCharacter()
- if let nextTwo = peekNext(count: 2), nextTwo == "--" {
- skip(2)
- return switchTo(.CommentStart)
- } else if let nextSeven = peekNext(count: 7), nextSeven.uppercased() == "DOCTYPE" {
- skip(7)
- return switchTo(.DOCTYPE)
- } else if let nextSeven = peekNext(count: 7), nextSeven.uppercased() == "[CDATA[" {
- skip(7)
- // FIXME: If there is an adjusted current node and it is not an element in the HTML namespace,
- // then switch to the CDATA section state.
- // FIXME: log_parse_error()
- self.currentBuilder = "[CDATA["
- self.currentToken = HTMLToken(type: .Comment(data: ""))
- return switchTo(.BogusComment)
- } else {
- // FIXME: log_parse_error()
- self.currentToken = HTMLToken(type: .Comment(data: ""))
- return switchTo(.BogusComment)
- }
- // 13.2.5.43 Comment start state, https://html.spec.whatwg.org/multipage/parsing.html#comment-start-state
- case .CommentStart:
- switch currentInputCharacter {
- case "-":
- return switchTo(.CommentStartDash)
- case ">":
- // FIXME: log_parse_error()
- return switchToAndEmitCurrentToken(.Data)
- default:
- return reconsume(currentInputCharacter, in: .Comment)
- }
- // 13.2.5.44 Comment start dash state, https://html.spec.whatwg.org/multipage/parsing.html#comment-start-dash-state
- case .CommentStartDash:
- switch currentInputCharacter {
- case "-":
- return switchTo(.CommentEnd)
- case ">":
- // FIXME: log_parse_error()
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return emitCurrentTokenFollowedByEOF()
- default:
- currentBuilder.append("-")
- return reconsume(currentInputCharacter, in: .Comment)
- }
- // 13.2.5.45 Comment state, https://html.spec.whatwg.org/multipage/parsing.html#comment-state
- case .Comment:
- switch currentInputCharacter {
- case "<":
- currentBuilder.append("<")
- return switchTo(.CommentLessThanSign)
- case "-":
- return switchTo(.CommentEndDash)
- case "\0":
- // FIXME: log_parse_error()
- currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case nil:
- // FIXME: log_parse_error()
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return emitCurrentTokenFollowedByEOF()
- default:
- currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.46 Comment less-than sign state, https://html.spec.whatwg.org/multipage/parsing.html#comment-less-than-sign-state
- case .CommentLessThanSign:
- switch currentInputCharacter {
- case "!":
- currentBuilder.append(currentInputCharacter!)
- return switchTo(.CommentLessThanSignBang)
- case "<":
- currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- default:
- return reconsume(currentInputCharacter, in: .Comment)
- }
- // 13.2.5.47 Comment less-than sign bang state, https://html.spec.whatwg.org/multipage/parsing.html#comment-less-than-sign-bang-state
- case .CommentLessThanSignBang:
- switch currentInputCharacter {
- case "-":
- return switchTo(.CommentLessThanSignBangDash)
- default:
- return reconsume(currentInputCharacter, in: .Comment)
- }
- // 13.2.5.48 Comment less-than sign bang dash state, https://html.spec.whatwg.org/multipage/parsing.html#comment-less-than-sign-bang-dash-state
- case .CommentLessThanSignBangDash:
- switch currentInputCharacter {
- case "-":
- return switchTo(.CommentLessThanSignBangDashDash)
- default:
- return reconsume(currentInputCharacter, in: .CommentEndDash)
- }
- // 13.2.5.49 Comment less-than sign bang dash dash state, https://html.spec.whatwg.org/multipage/parsing.html#comment-less-than-sign-bang-dash-dash-state
- case .CommentLessThanSignBangDashDash:
- switch currentInputCharacter {
- case ">", nil:
- return reconsume(currentInputCharacter, in: .CommentEnd)
- default:
- // FIXME: log_parse_error()
- return reconsume(currentInputCharacter, in: .CommentEnd)
- }
- // 13.2.5.50 Comment end dash state, https://html.spec.whatwg.org/multipage/parsing.html#comment-end-dash-state
- case .CommentEndDash:
- switch currentInputCharacter {
- case "-":
- return switchTo(.CommentEnd)
- case nil:
- // FIXME: log_parse_error()
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return emitCurrentTokenFollowedByEOF()
- default:
- currentBuilder.append("-")
- return reconsume(currentInputCharacter, in: .Comment)
- }
- // 13.2.5.51 Comment end state, https://html.spec.whatwg.org/multipage/parsing.html#comment-end-state
- case .CommentEnd:
- switch currentInputCharacter {
- case ">":
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return switchToAndEmitCurrentToken(.Data)
- case "!":
- return switchTo(.CommentEndBang)
- case "-":
- currentBuilder.append("-")
- return continueInCurrentState()
- case nil:
- // FIXME: log_parse_error()
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return emitCurrentTokenFollowedByEOF()
- default:
- currentBuilder.append("--")
- return reconsume(currentInputCharacter, in: .Comment)
- }
- // 13.2.5.52 Comment end bang state, https://html.spec.whatwg.org/multipage/parsing.html#comment-end-bang-state
- case .CommentEndBang:
- switch currentInputCharacter {
- case "-":
- currentBuilder.append("--!")
- return switchTo(.CommentEndDash)
- case ">":
- // FIXME: log_parse_error()
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- currentToken = HTMLToken(type: .Comment(data: currentBuilder.takeString()))
- return emitCurrentTokenFollowedByEOF()
- default:
- currentBuilder.append("--!")
- return reconsume(currentInputCharacter, in: .Comment)
- }
- // 13.2.5.53 DOCTYPE state, https://html.spec.whatwg.org/multipage/parsing.html#doctype-state
- case .DOCTYPE:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return switchTo(.BeforeDOCTYPEName)
- case ">":
- return reconsume(currentInputCharacter, in: .BeforeDOCTYPEName)
- case nil:
- // FIXME: log_parse_error()
- currentToken = HTMLToken(type: .DOCTYPE(name: nil, publicIdentifier: nil, systemIdentifier: nil, forceQuirksMode: true))
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- return reconsume(currentInputCharacter!, in: .BeforeDOCTYPEName)
- }
- // 13.2.5.54 Before DOCTYPE name state, https://html.spec.whatwg.org/multipage/parsing.html#before-doctype-name-state
- case .BeforeDOCTYPEName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- precondition(self.currentBuilder.isEmpty)
- self.currentToken = HTMLToken(type: .DOCTYPE(name: nil, publicIdentifier: nil, systemIdentifier: nil, forceQuirksMode: false))
- self.currentBuilder.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- return switchTo(.DOCTYPEName)
- case "\0":
- // FIXME: log_parse_error()
- precondition(self.currentBuilder.isEmpty)
- self.currentToken = HTMLToken(type: .DOCTYPE(name: nil, publicIdentifier: nil, systemIdentifier: nil, forceQuirksMode: false))
- self.currentBuilder.append("\u{FFFD}")
- return switchTo(.DOCTYPEName)
- case ">":
- // FIXME: log_parse_error()
- self.currentToken = HTMLToken(type: .DOCTYPE(name: nil, publicIdentifier: nil, systemIdentifier: nil, forceQuirksMode: true))
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken = HTMLToken(type: .DOCTYPE(name: nil, publicIdentifier: nil, systemIdentifier: nil, forceQuirksMode: true))
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- precondition(self.currentBuilder.isEmpty)
- self.currentToken = HTMLToken(type: .DOCTYPE(name: nil, publicIdentifier: nil, systemIdentifier: nil, forceQuirksMode: false))
- self.currentBuilder.append(currentInputCharacter!)
- return switchTo(.DOCTYPEName)
- }
- // 13.2.5.55 DOCTYPE name state, https://html.spec.whatwg.org/multipage/parsing.html#doctype-name-state
- case .DOCTYPEName:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- self.currentToken.name = self.currentBuilder.takeString()
- return switchTo(.AfterDOCTYPEName)
- case ">":
- self.currentToken.name = self.currentBuilder.takeString()
- return switchToAndEmitCurrentToken(.Data)
- case let c? where HTMLTokenizer.asciiUpperAlpha.contains(c.unicodeScalars.first!):
- self.currentBuilder.append(Character(Unicode.Scalar(c.asciiValue! + 0x20)))
- return continueInCurrentState()
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- self.currentToken.name = self.currentBuilder.takeString()
- return emitCurrentTokenFollowedByEOF()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.56 After DOCTYPE name state, https://html.spec.whatwg.org/multipage/parsing.html#after-doctype-name-state
- case .AfterDOCTYPEName:
- precondition(self.currentBuilder.isEmpty)
- precondition(self.currentToken.name != nil)
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case ">":
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- if "pP".contains(currentInputCharacter!), peekNext(count: 5)?.uppercased() == "UBLIC" {
- skip(5)
- return switchTo(.AfterDOCTYPEPublicKeyword)
- }
- if "sS".contains(currentInputCharacter!), peekNext(count: 5)?.uppercased() == "YSTEM" {
- skip(5)
- return switchTo(.AfterDOCTYPESystemKeyword)
- }
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return reconsume(currentInputCharacter!, in: .BogusDOCTYPE)
- }
- // 13.2.5.57 After DOCTYPE public keyword state, https://html.spec.whatwg.org/multipage/parsing.html#after-doctype-public-keyword-state
- case .AfterDOCTYPEPublicKeyword:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return switchTo(.BeforeDOCTYPEPublicIdentifier)
- case "\"":
- // FIXME: log_parse_error()
- self.currentToken.publicIdentifier = ""
- return switchTo(.DOCTYPEPublicIdentifierDoubleQuoted)
- case "'":
- self.currentToken.publicIdentifier = ""
- return switchTo(.DOCTYPEPublicIdentifierSingleQuoted)
- case ">":
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return reconsume(currentInputCharacter!, in: .BogusDOCTYPE)
- }
- // 13.2.5.58 Before DOCTYPE public identifier state, https://html.spec.whatwg.org/multipage/parsing.html#before-doctype-public-identifier-state
- case .BeforeDOCTYPEPublicIdentifier:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case "\"":
- self.currentToken.publicIdentifier = ""
- return switchTo(.DOCTYPEPublicIdentifierDoubleQuoted)
- case "'":
- self.currentToken.publicIdentifier = ""
- return switchTo(.DOCTYPEPublicIdentifierSingleQuoted)
- case ">":
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return reconsume(currentInputCharacter!, in: .BogusDOCTYPE)
- }
- // 13.2.5.59 DOCTYPE public identifier (double-quoted) state, https://html.spec.whatwg.org/multipage/parsing.html#doctype-public-identifier-double-quoted-state
- case .DOCTYPEPublicIdentifierDoubleQuoted:
- switch currentInputCharacter {
- case "\"":
- self.currentToken.publicIdentifier = self.currentBuilder.takeString()
- return switchTo(.AfterDOCTYPEPublicIdentifier)
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case ">":
- // FIXME: log_parse_error()
- self.currentToken.publicIdentifier = self.currentBuilder.takeString()
- self.currentToken.forceQuirks = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.60 DOCTYPE public identifier (single-quoted) state, https://html.spec.whatwg.org/multipage/parsing.html#doctype-public-identifier-single-quoted-state
- case .DOCTYPEPublicIdentifierSingleQuoted:
- switch currentInputCharacter {
- case "'":
- self.currentToken.publicIdentifier = self.currentBuilder.takeString()
- return switchTo(.AfterDOCTYPEPublicIdentifier)
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case ">":
- // FIXME: log_parse_error()
- self.currentToken.publicIdentifier = self.currentBuilder.takeString()
- self.currentToken.forceQuirks = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.61 After DOCTYPE public identifier state, https://html.spec.whatwg.org/multipage/parsing.html#after-doctype-public-identifier-state
- case .AfterDOCTYPEPublicIdentifier:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return switchTo(.BetweenDOCTYPEPublicAndSystemIdentifiers)
- case ">":
- return switchToAndEmitCurrentToken(.Data)
- case "\"":
- // FIXME: log_parse_error()
- self.currentToken.systemIdentifier = ""
- return switchTo(.DOCTYPESystemIdentifierDoubleQuoted)
- case "'":
- // FIXME: log_parse_error()
- self.currentToken.systemIdentifier = ""
- return switchTo(.DOCTYPESystemIdentifierSingleQuoted)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return reconsume(currentInputCharacter!, in: .BogusDOCTYPE)
- }
- // 13.2.5.62 Between DOCTYPE public and system identifiers state, https://html.spec.whatwg.org/multipage/parsing.html#between-doctype-public-and-system-identifiers-state
- case .BetweenDOCTYPEPublicAndSystemIdentifiers:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case ">":
- return switchToAndEmitCurrentToken(.Data)
- case "\"":
- self.currentToken.systemIdentifier = ""
- return switchTo(.DOCTYPESystemIdentifierDoubleQuoted)
- case "'":
- self.currentToken.systemIdentifier = ""
- return switchTo(.DOCTYPESystemIdentifierSingleQuoted)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return reconsume(currentInputCharacter!, in: .BogusDOCTYPE)
- }
- // 13.2.5.63 After DOCTYPE system keyword state, https://html.spec.whatwg.org/multipage/parsing.html#after-doctype-system-keyword-state
- case .AfterDOCTYPESystemKeyword:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return switchTo(.BeforeDOCTYPESystemIdentifier)
- case "\"":
- // FIXME: log_parse_error()
- self.currentToken.systemIdentifier = ""
- return switchTo(.DOCTYPESystemIdentifierDoubleQuoted)
- case "'":
- // FIXME: log_parse_error()
- self.currentToken.systemIdentifier = ""
- return switchTo(.DOCTYPESystemIdentifierSingleQuoted)
- case ">":
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return reconsume(currentInputCharacter!, in: .BogusDOCTYPE)
- }
- // 13.2.5.64 Before DOCTYPE system identifier state, https://html.spec.whatwg.org/multipage/parsing.html#before-doctype-system-identifier-state
- case .BeforeDOCTYPESystemIdentifier:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case "\"":
- self.currentToken.systemIdentifier = ""
- return switchTo(.DOCTYPESystemIdentifierDoubleQuoted)
- case "'":
- self.currentToken.systemIdentifier = ""
- return switchTo(.DOCTYPESystemIdentifierSingleQuoted)
- case ">":
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return reconsume(currentInputCharacter!, in: .BogusDOCTYPE)
- }
- // 13.2.5.65 DOCTYPE system identifier (double-quoted) state, https://html.spec.whatwg.org/multipage/parsing.html#doctype-system-identifier-double-quoted-state
- case .DOCTYPESystemIdentifierDoubleQuoted:
- switch currentInputCharacter {
- case "\"":
- self.currentToken.systemIdentifier = self.currentBuilder.takeString()
- return switchTo(.AfterDOCTYPESystemIdentifier)
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case ">":
- // FIXME: log_parse_error()
- self.currentToken.systemIdentifier = self.currentBuilder.takeString()
- self.currentToken.forceQuirks = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.66 DOCTYPE system identifier (single-quoted) state, https://html.spec.whatwg.org/multipage/parsing.html#doctype-system-identifier-single-quoted-state
- case .DOCTYPESystemIdentifierSingleQuoted:
- switch currentInputCharacter {
- case "'":
- return switchTo(.AfterDOCTYPESystemIdentifier)
- case "\0":
- // FIXME: log_parse_error()
- self.currentBuilder.append("\u{FFFD}")
- return continueInCurrentState()
- case ">":
- // FIXME: log_parse_error()
- self.currentToken.systemIdentifier = self.currentBuilder.takeString()
- self.currentToken.forceQuirks = true
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- // 13.2.5.67 After DOCTYPE system identifier state, https://html.spec.whatwg.org/multipage/parsing.html#after-doctype-system-identifier-state
- case .AfterDOCTYPESystemIdentifier:
- switch currentInputCharacter {
- case "\t", "\n", "\u{000C}", " ":
- return continueInCurrentState()
- case ">":
- return switchToAndEmitCurrentToken(.Data)
- case nil:
- // FIXME: log_parse_error()
- self.currentToken.forceQuirks = true
- return emitCurrentTokenFollowedByEOF()
- default:
- // FIXME: log_parse_error()
- // NOTE: This does not set the current DOCTYPE token's force-quirks flag to on.
- return reconsume(currentInputCharacter!, in: .BogusDOCTYPE)
- }
- // 13.2.5.68 Bogus DOCTYPE state, https://html.spec.whatwg.org/multipage/parsing.html#bogus-doctype-state
- case .BogusDOCTYPE:
- switch currentInputCharacter {
- case ">":
- return switchToAndEmitCurrentToken(.Data)
- case "\0":
- // FIXME: log_parse_error()
- return continueInCurrentState()
- case nil:
- return emitCurrentTokenFollowedByEOF()
- default:
- return continueInCurrentState()
- }
- // 13.2.5.69 CDATA section state, https://html.spec.whatwg.org/multipage/parsing.html#cdata-section-state
- case .CDATASection:
- switch currentInputCharacter {
- case "]":
- return switchTo(.CDATASectionBracket)
- case nil:
- // FIXME: log_parse_error()
- return emitEOF()
- default:
- // NOTE: U+0000 NULL characters are handled in the tree construction stage,
- // as part of the in foreign content insertion mode, which is the only place where CDATA sections can appear.
- return emitCharacter(currentInputCharacter!)
- }
- // 13.2.5.70 CDATA section bracket state, https://html.spec.whatwg.org/multipage/parsing.html#cdata-section-bracket-state
- case .CDATASectionBracket:
- switch currentInputCharacter {
- case "]":
- return switchTo(.CDATASectionEnd)
- default:
- return emitCharacterAndReconsume("]", in: .CDATASection, currentInputCharacter: currentInputCharacter)
- }
- // 13.2.5.71 CDATA section end state, https://html.spec.whatwg.org/multipage/parsing.html#cdata-section-end-state
- case .CDATASectionEnd:
- switch currentInputCharacter {
- case "]":
- return emitCharacter("]")
- case ">":
- return switchTo(.Data)
- default:
- queuedTokens.append(HTMLToken(type: .Character(codePoint: "]")))
- return emitCharacterAndReconsume("]", in: .CDATASection, currentInputCharacter: currentInputCharacter)
- }
- // 13.2.5.72 Character reference state, https://html.spec.whatwg.org/multipage/parsing.html#character-reference-state
- case .CharacterReference:
- self.temporaryBuffer = "&"
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiAlphanumeric.contains(c.unicodeScalars.first!):
- return reconsume(currentInputCharacter!, in: .NamedCharacterReference)
- case "#":
- self.temporaryBuffer.append(currentInputCharacter!)
- return switchTo(.NumericCharacterReference)
- default:
- self.flushCodepointsConsumedAsACharacterReference()
- return reconsume(currentInputCharacter, in: self.returnState)
- }
- // 13.2.5.73 Named character reference state, https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state
- case .NamedCharacterReference:
- var subString = self.input[self.previousCursor...]
- let entityMatch = subString.withUTF8 { utf8 in
- return Web.HTML.match_entity_for_named_character_reference(AK.StringView(utf8.baseAddress!, utf8.count))
- }
- if entityMatch.hasValue {
- let entity = entityMatch.value!.entity
- skip(entity.length())
- // FIXME: Iterate over the entity's code points and add them instead of creating a string
- self.temporaryBuffer.append(Swift.String(akStringView: entity)!)
- if self.consumedAsPartOfAnAttribute(), !entity.endsWith(";") {
- if let peeked = peekCodePoint(), peeked == "=" || HTMLTokenizer.asciiAlphanumeric.contains(peeked.unicodeScalars.first!) {
- self.flushCodepointsConsumedAsACharacterReference()
- return switchTo(self.returnState)
- }
- }
- if !entity.endsWith(";") {
- // FIXME: log_parse_error()
- }
- self.temporaryBuffer = ""
- // FIXME: This AK::Vector<u32, 2> should be CxxConvertibleToContainer, but https://github.com/swiftlang/swift/issues/77607
- let codePoints = entityMatch.value!.code_points
- for i in 0...codePoints.size() {
- self.temporaryBuffer.append(Character(Unicode.Scalar(codePoints[i])!))
- }
- self.flushCodepointsConsumedAsACharacterReference()
- return switchTo(self.returnState)
- }
- self.flushCodepointsConsumedAsACharacterReference()
- return reconsume(currentInputCharacter, in: .AmbiguousAmpersand)
- // 13.2.5.74 Ambiguous ampersand state, https://html.spec.whatwg.org/multipage/parsing.html#ambiguous-ampersand-state
- case .AmbiguousAmpersand:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiAlphanumeric.contains(c.unicodeScalars.first!):
- if self.consumedAsPartOfAnAttribute() {
- self.currentBuilder.append(currentInputCharacter!)
- return continueInCurrentState()
- }
- return emitCharacter(currentInputCharacter!)
- case ";":
- // FIXME: log_parse_error()
- return reconsume(currentInputCharacter!, in: self.returnState)
- default:
- return reconsume(currentInputCharacter, in: self.returnState)
- }
- // 13.2.5.75 Numeric character reference state, https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-state
- case .NumericCharacterReference:
- self.characterReferenceCode = 0
- switch currentInputCharacter {
- case "x", "X":
- self.temporaryBuffer.append(currentInputCharacter!)
- return switchTo(.HexadecimalCharacterReferenceStart)
- default:
- return reconsume(currentInputCharacter, in: .DecimalCharacterReferenceStart)
- }
- // 13.2.5.76 Hexadecimal character reference start state, https://html.spec.whatwg.org/multipage/parsing.html#hexadecimal-character-reference-start-state
- case .HexadecimalCharacterReferenceStart:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiHexDigit.contains(c.unicodeScalars.first!):
- return reconsume(currentInputCharacter!, in: .HexadecimalCharacterReference)
- default:
- // FIXME: log_parse_error()
- self.flushCodepointsConsumedAsACharacterReference()
- return reconsume(currentInputCharacter, in: self.returnState)
- }
- // 13.2.5.77 Decimal character reference start state, https://html.spec.whatwg.org/multipage/parsing.html#decimal-character-reference-start-state
- case .DecimalCharacterReferenceStart:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiDigit.contains(c.unicodeScalars.first!):
- return reconsume(currentInputCharacter!, in: .DecimalCharacterReference)
- default:
- // FIXME: log_parse_error()
- self.flushCodepointsConsumedAsACharacterReference()
- return reconsume(currentInputCharacter, in: self.returnState)
- }
- // 13.2.5.78 Hexadecimal character reference state, https://html.spec.whatwg.org/multipage/parsing.html#hexadecimal-character-reference-state
- case .HexadecimalCharacterReference:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiDigit.contains(c.unicodeScalars.first!):
- self.characterReferenceCode = self.characterReferenceCode * 16 + Int(c.asciiValue! - 0x30)
- return continueInCurrentState()
- case let c? where HTMLTokenizer.asciiUpperHexDigit.contains(c.unicodeScalars.first!):
- self.characterReferenceCode = self.characterReferenceCode * 16 + Int(c.asciiValue! - 0x37)
- return continueInCurrentState()
- case let c? where HTMLTokenizer.asciiLowerHexDigit.contains(c.unicodeScalars.first!):
- self.characterReferenceCode = self.characterReferenceCode * 16 + Int(c.asciiValue! - 0x57)
- return continueInCurrentState()
- case ";":
- return switchTo(.NumericCharacterReferenceEnd)
- default:
- // FIXME: log_parse_error()
- return reconsume(currentInputCharacter, in: .NumericCharacterReferenceEnd)
- }
- // 13.2.5.79 Decimal character reference state, https://html.spec.whatwg.org/multipage/parsing.html#decimal-character-reference-state
- case .DecimalCharacterReference:
- switch currentInputCharacter {
- case let c? where HTMLTokenizer.asciiDigit.contains(c.unicodeScalars.first!):
- self.characterReferenceCode = self.characterReferenceCode * 10 + Int(c.asciiValue! - 0x30)
- return continueInCurrentState()
- case ";":
- return switchTo(.NumericCharacterReferenceEnd)
- default:
- // FIXME: log_parse_error()
- return reconsume(currentInputCharacter, in: .NumericCharacterReferenceEnd)
- }
- // 13.2.5.80 Numeric character reference end state, https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state
- case .NumericCharacterReferenceEnd:
- dontConsumeNextInputCharacter()
- let codePoint: UnicodeScalar =
- switch self.characterReferenceCode {
- case 0x00:
- // FIXME: log_parse_error()
- UnicodeScalar(0xFFFD)!
- case let c where c > 0x10FFFF:
- // FIXME: log_parse_error()
- UnicodeScalar(0xFFFD)!
- case let c where UTF16.CodeUnit(exactly: c).map({ UTF16.isSurrogate($0) }) != nil:
- // FIXME: log_parse_error()
- UnicodeScalar(0xFFFD)!
- case let c where UnicodeScalar(c)! == "\u{000D}" || HTMLTokenizer.controlNotAsciiWhitespace.contains(UnicodeScalar(c)!):
- // FIXME: log_parse_error()
- if let codePoint = HTMLTokenizer.characterReferenceControlCodeMapping[c] {
- codePoint
- } else {
- UnicodeScalar(c)!
- }
- case let c where HTMLTokenizer.nonCharacter.contains(UnicodeScalar(c)!):
- // FIXME: log_parse_error()
- UnicodeScalar(c)!
- default:
- UnicodeScalar(self.characterReferenceCode)!
- }
- self.temporaryBuffer = Swift.String(Character(codePoint))
- self.flushCodepointsConsumedAsACharacterReference()
- return switchTo(self.returnState)
- }
- }
- }
|