diff --git a/HTMLKit/HTMLParser.h b/HTMLKit/HTMLParser.h index 61dc920..4313bf1 100644 --- a/HTMLKit/HTMLParser.h +++ b/HTMLKit/HTMLParser.h @@ -10,4 +10,6 @@ @interface HTMLParser : NSObject +- (id)adjustedCurrentNode; + @end diff --git a/HTMLKit/HTMLToken.h b/HTMLKit/HTMLToken.h index 060d112..f087a64 100644 --- a/HTMLKit/HTMLToken.h +++ b/HTMLKit/HTMLToken.h @@ -47,6 +47,14 @@ typedef NS_ENUM(NSUInteger, HTMLTokenType) */ @interface HTMLDOCTYPEToken : HTMLToken +@property (nonatomic, strong) NSMutableString *publicIdentifier; +@property (nonatomic, strong) NSMutableString *systemIdentifier; +@property (nonatomic, assign) BOOL forceQuirks; + +- (instancetype)initWithName:(NSString *)string; + +- (void)appendCharacterToName:(UTF32Char)character; + @end #pragma mark - HTMLTagToken @@ -57,6 +65,13 @@ typedef NS_ENUM(NSUInteger, HTMLTokenType) */ @interface HTMLTagToken : HTMLToken +@property (nonatomic, copy) NSString *tagName; +@property (nonatomic, assign) BOOL selfClosing; + +- (instancetype)initWithTagName:(NSString *)tagName; + +- (void)appendCharacterToTagName:(UTF32Char)character; + @end #pragma mark - HTMLStartTagToken @@ -87,6 +102,11 @@ typedef NS_ENUM(NSUInteger, HTMLTokenType) */ @interface HTMLCommentToken : HTMLToken +- (instancetype)initWithData:(NSString *)data; + +- (void)appendCharacterToData:(UTF32Char)character; +- (void)appendStringToData:(NSString *)string; + @end #pragma mark - HTMLCharacterToken @@ -98,7 +118,10 @@ typedef NS_ENUM(NSUInteger, HTMLTokenType) @interface HTMLCharacterToken : HTMLToken - (instancetype)initWithCharacter:(UTF32Char)character; +- (instancetype)initWithString:(NSString *)string; + - (void)appendCharacter:(UTF32Char)character; +- (void)appendString:(NSString *)string; @end diff --git a/HTMLKit/HTMLTokenizer.m b/HTMLKit/HTMLTokenizer.m index 7c40b7c..c9bdb46 100644 --- a/HTMLKit/HTMLTokenizer.m +++ b/HTMLKit/HTMLTokenizer.m @@ -7,6 +7,7 @@ // #import "HTMLTokenizer.h" +#import "HTMLParser.h" #import "HTMLToken.h" #import "HTMLTokenizerStates.h" #import "HTMLTokenizerCharacters.h" @@ -16,8 +17,27 @@ NSMutableDictionary *_states; HTMLTokenizerState _currentState; + /* Parser */ + HTMLParser *_parser; + + /* Input Stream & Tokens Queue */ HTMLInputStreamReader *_inputStreamReader; NSMutableArray *_tokens; + + /* Character Reference */ + HTMLTokenizerState _previousTokenizerState; + UTF32Char _additionalAllowedCharacter; + + /* Pending Tokens & Attributes*/ + HTMLTagToken *_currentTagToken; + HTMLCommentToken *_currentCommentToken; + HTMLDOCTYPEToken *_currentDoctypeToken; + NSMutableString *_currentAttributeName; + NSMutableString *_currentAttributeValue; + + /* Aux */ + NSString *_lastStartTagName; + NSMutableString *_temporaryBuffer; } @end @@ -66,11 +86,22 @@ _currentState = state; } +- (void)switchToState:(HTMLTokenizerState)state withAdditionalAllowedCharacter:(UTF32Char)character +{ + _previousTokenizerState = _currentState; + _additionalAllowedCharacter = character; + [self switchToState:state]; +} + #pragma mark - Emits - (void)emitToken:(HTMLToken *)token { [_tokens addObject:token]; + + if (token.isStartTagToken) { + _lastStartTagName = [(HTMLStartTagToken *)token tagName]; + } } - (void)emitEOFToken @@ -78,13 +109,25 @@ [self emitToken:[HTMLEOFToken new]]; } +- (void)emitCurrentTagToken +{ + [self finalizeCurrentAttribute]; + [self emitToken:_currentTagToken]; + _currentTagToken = nil; +} + - (void)emitCharacterToken:(UTF32Char)character +{ + [self emitCharacterTokenWithString:StringFromUTF32Char(character)]; +} + +- (void)emitCharacterTokenWithString:(NSString *)string { HTMLEOFToken *previousToken = [_tokens lastObject]; if ([previousToken isCharacterToken]) { - [(HTMLCharacterToken *)previousToken appendCharacter:character]; + [(HTMLCharacterToken *)previousToken appendString:string]; } else { - [self emitToken:[[HTMLCharacterToken alloc] initWithCharacter:character]]; + [self emitToken:[[HTMLCharacterToken alloc] initWithString:string]]; } } @@ -98,15 +141,32 @@ [self emitToken:token]; } +#pragma mark - Token Checks + +- (BOOL)isCurrentEndTagTokenAppropriate +{ + return ([_currentTagToken isKindOfClass:[HTMLEndTagToken class]] && + [_currentTagToken.tagName isEqualToString:_lastStartTagName]); +} + +#pragma mark - Attributes + +- (void)finalizeCurrentAttribute +{ +#warning Implement attributes for Tag Tokens +} + #pragma mark - Consume Character Reference -- (NSString *)consumeCharachterReferenceWithAddtionalAllowedCharacter:(UTF32Char)additionalAllowedCharacter +- (NSString *)attemptToConsumeCharachterReferenceWithAddtionalAllowedCharacter:(UTF32Char)additionalAllowedCharacter { UTF32Char character = [_inputStreamReader nextInputCharacter]; if (additionalAllowedCharacter != (UTF32Char)EOF && character == additionalAllowedCharacter) { return nil; } + [_inputStreamReader markCurrentLocation]; + switch (character) { case CHARACTER_TABULATION: case LINE_FEED: @@ -118,18 +178,17 @@ return nil; case NUMBER_SIGN: { - NSString *numberReference = [self consumeNumberCharacterReference]; + NSString *numberReference = [self attemptToConsumeNumberCharacterReference]; return numberReference; } default: +#warning Implement named entity replacement return nil; } } -- (NSString *)consumeNumberCharacterReference +- (NSString *)attemptToConsumeNumberCharacterReference { - [_inputStreamReader markCurrentLocation]; - [_inputStreamReader consumeNextInputCharacter]; UTF32Char character = [_inputStreamReader nextInputCharacter]; @@ -143,7 +202,7 @@ success = [_inputStreamReader consumeHexInt:&number]; break; default: - success = [_inputStreamReader consumeHexInt:&number]; + success = [_inputStreamReader consumeUnsignedInt:&number]; break; } @@ -177,336 +236,1931 @@ - (void)HTMLTokenizerStateData { + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case AMPERSAND: + [self switchToState:HTMLTokenizerStateCharacterReferenceInData withAdditionalAllowedCharacter:EOF]; + break; + case LESS_THAN_SIGN: + [self switchToState:HTMLTokenizerStateTagOpen]; + break; + case NULL_CHAR: + [self emitParseError:@"U+0000 NULL character in Data State"]; + [self emitCharacterToken:character]; + break; + case EOF: + [self emitEOFToken]; + break; + default: + [self emitCharacterToken:character]; + break; + } } - (void)HTMLTokenizerStateCharacterReferenceInData { + [self switchToState:HTMLTokenizerStateData]; + NSString *characterReference = [self attemptToConsumeCharachterReferenceWithAddtionalAllowedCharacter:_additionalAllowedCharacter]; + if (characterReference == nil) { + [self emitCharacterToken:AMPERSAND]; + } else { + [self emitCharacterTokenWithString:characterReference]; + } } - (void)HTMLTokenizerStateRCDATA { + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case AMPERSAND: + [self switchToState:HTMLTokenizerStateCharacterReferenceInRCDATA]; + break; + case LESS_THAN_SIGN: + [self switchToState:HTMLTokenizerStateRCDATALessThanSign]; + break; + case NULL_CHAR: + [self emitParseError:@"U+0000 NULL character in RCDATA state"]; + [self emitCharacterToken:REPLACEMENT_CHAR]; + break; + case EOF: + [self emitEOFToken]; + break; + default: + [self emitCharacterToken:character]; + break; + } } - (void)HTMLTokenizerStateCharacterReferenceInRCDATA { + [self switchToState:HTMLTokenizerStateRCDATA]; + NSString *characterReference = [self attemptToConsumeCharachterReferenceWithAddtionalAllowedCharacter:(UTF32Char)EOF]; + if (characterReference == nil) { + [self emitCharacterToken:AMPERSAND]; + } else { + [self emitCharacterTokenWithString:characterReference]; + } } - (void)HTMLTokenizerStateRAWTEXT { + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case LESS_THAN_SIGN: + [self switchToState:HTMLTokenizerStateRAWTEXTLessThanSign]; + break; + case NULL_CHAR: + [self emitParseError:@"U+0000 NULL character in RAWTEXT state"]; + [self emitCharacterToken:REPLACEMENT_CHAR]; + break; + case EOF: + [self emitEOFToken]; + break; + default: + [self emitCharacterToken:character]; + break; + } } - (void)HTMLTokenizerStateScriptData { + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case LESS_THAN_SIGN: + [self switchToState:HTMLTokenizerStateScriptDataLessThanSign]; + break; + case NULL_CHAR: + [self emitParseError:@"U+0000 NULL character in Script Data state"]; + [self emitCharacterToken:REPLACEMENT_CHAR]; + break; + case EOF: + [self emitEOFToken]; + break; + default: + [self emitCharacterToken:character]; + break; + } } - (void)HTMLTokenizerStatePLAINTEXT { + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case NULL_CHAR: + [self emitParseError:@"U+0000 NULL character in PLAINTEXT state"]; + [self emitCharacterToken:REPLACEMENT_CHAR]; + break; + case EOF: + [self emitEOFToken]; + break; + default: + [self emitCharacterToken:character]; + break; + } } - (void)HTMLTokenizerStateTagOpen { + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case EXCLAMATION_MARK: + [self switchToState:HTMLTokenizerStateMarkupDeclarationOpen]; + break; + case SOLIDUS: + [self switchToState:HTMLTokenizerStateEndTagOpen]; + break; + case LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z: + _currentTagToken = [[HTMLStartTagToken alloc] initWithTagName:StringFromUniChar(character + 0x0020)]; + [self switchToState:HTMLTokenizerStateTagName]; + break; + case LATIN_SMALL_LETTER_A ... LATIN_SMALL_LETTER_Z: + _currentTagToken = [[HTMLStartTagToken alloc] initWithTagName:StringFromUniChar(character)]; + [self switchToState:HTMLTokenizerStateTagName]; + break; + case QUESTION_MARK: + [self emitParseError:@"0x003F Bogus ? in Tag Open state"]; + [self switchToState:HTMLTokenizerStateBogusComment]; + break; + default: + [self emitParseError:@"%X Unexpected character in Tag Open state", character]; + [self switchToState:HTMLTokenizerStateData]; + [self emitCharacterToken:LESS_THAN_SIGN]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + } } - (void)HTMLTokenizerStateEndTagOpen { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z: + _currentTagToken = [[HTMLEndTagToken alloc] initWithTagName:StringFromUniChar(character + 0x0020)]; + [self switchToState:HTMLTokenizerStateTagName]; + break; + case LATIN_SMALL_LETTER_A ... LATIN_SMALL_LETTER_Z: + _currentTagToken = [[HTMLEndTagToken alloc] initWithTagName:StringFromUniChar(character)]; + [self switchToState:HTMLTokenizerStateTagName]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"Unexpected 0x003E > in End Tag Open state"]; + [self switchToState:HTMLTokenizerStateData]; + break; + case EOF: + [self emitParseError:@"EOF reached in End Tag Open state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitCharacterTokenWithString:@" character in Before Attribute Value state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitCurrentTagToken]; + return; + case LESS_THAN_SIGN: + case EQUALS_SIGN: + case GRAVE_ACCENT: + [self emitParseError:@"%C unexpected character in Before Attribute Value state", (unichar)character]; + break; + case EOF: + [self emitParseError:@"EOF reached in Before Attribute Value state"]; + [self switchToState:HTMLTokenizerStateData]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + return; + } + [_currentAttributeValue appendString:StringFromUTF32Char(character)]; + [self switchToState:HTMLTokenizerStateAttributeValueUnquoted]; } - (void)HTMLTokenizerStateAttributeValueDoubleQuoted { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case QUESTION_MARK: + [self switchToState:HTMLTokenizerStateAfterAttributeValueQuoted]; + break; + case AMPERSAND: + [self switchToState:HTMLTokenizerStateCharacterReferenceInAttributeValue withAdditionalAllowedCharacter:QUOTATION_MARK]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Attribute Value Double-Quoted state"]; + [_currentAttributeValue appendString:StringFromUniChar(REPLACEMENT_CHAR)]; + break; + case EOF: + [self emitParseError:@"EOF reached in Attribute Value Double-Quoted state"]; + [self switchToState:HTMLTokenizerStateData]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentAttributeValue appendString:StringFromUTF32Char(character)]; + break; + } } - (void)HTMLTokenizerStateAttributeValueSingleQuoted { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case APOSTROPHE: + [self switchToState:HTMLTokenizerStateAfterAttributeValueQuoted]; + break; + case AMPERSAND: + [self switchToState:HTMLTokenizerStateCharacterReferenceInAttributeValue withAdditionalAllowedCharacter:APOSTROPHE]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Attribute Value Single-Quoted state"]; + [_currentAttributeValue appendString:StringFromUniChar(REPLACEMENT_CHAR)]; + break; + case EOF: + [self emitParseError:@"EOF reached in Attribute Value Single-Quoted state"]; + [self switchToState:HTMLTokenizerStateData]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentAttributeValue appendString:StringFromUTF32Char(character)]; + break; + } } - (void)HTMLTokenizerStateAttributeValueUnquoted { + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + [self switchToState:HTMLTokenizerStateBeforeAttributeName]; + return; + case AMPERSAND: + [self switchToState:HTMLTokenizerStateCharacterReferenceInAttributeValue withAdditionalAllowedCharacter:GREATER_THAN_SIGN]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + return; + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitCurrentTagToken]; + return; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Attribute Value Unquoted state"]; + [_currentAttributeValue appendString:StringFromUniChar(REPLACEMENT_CHAR)]; + return; + case QUOTATION_MARK: + case APOSTROPHE: + case LESS_THAN_SIGN: + case EQUALS_SIGN: + case GRAVE_ACCENT: + [self emitParseError:@"%C unexpected character in Attribute Value Unquoted state", (unichar)character]; + break; + case EOF: + [self emitParseError:@"EOF reached in Attribute Value Unquoted state"]; + [self switchToState:HTMLTokenizerStateData]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + return; + } + [_currentAttributeValue appendString:StringFromUTF32Char(character)]; } - (void)HTMLTokenizerStateCharacterReferenceInAttributeValue { + NSString *characterReference = [self attemptToConsumeCharachterReferenceWithAddtionalAllowedCharacter:_additionalAllowedCharacter]; + if (characterReference == nil) { + [_currentAttributeValue appendString:StringFromUniChar(AMPERSAND)]; + } else { + [_currentAttributeValue appendString:characterReference]; + } + [self switchToState:_previousTokenizerState]; } - (void)HTMLTokenizerStateAfterAttributeValueQuoted { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + [self switchToState:HTMLTokenizerStateBeforeAttributeName]; + break; + case SOLIDUS: + [self switchToState:HTMLTokenizerStateSelfClosingStartTag]; + break; + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitCurrentTagToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in After Attribute Value Quoted state"]; + [self switchToState:HTMLTokenizerStateData]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in After Attribute Value Quoted state", StringFromUTF32Char(character)]; + [self switchToState:HTMLTokenizerStateBeforeAttributeName]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + } } - (void)HTMLTokenizerStateSelfClosingStartTag { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case GREATER_THAN_SIGN: + _currentTagToken.selfClosing = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitCurrentTagToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in Self Closing Start Tag state"]; + [self switchToState:HTMLTokenizerStateData]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in Self Closing Start Tag state", StringFromUTF32Char(character)]; + [self switchToState:HTMLTokenizerStateBeforeAttributeName]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + } } - (void)HTMLTokenizerStateBogusComment { - + NSString *characters = [_inputStreamReader consumeCharactersUpToCharactersInString:@">"]; + characters = [characters stringByReplacingOccurrencesOfString:@"\0" withString:@"0xFFFD"]; + _currentCommentToken = [[HTMLCommentToken alloc] initWithData:characters]; + [self emitToken:_currentCommentToken]; + [self switchToState:HTMLTokenizerStateData]; +#warning Check if necessary + if ([_inputStreamReader consumeNextInputCharacter] == (UTF32Char)EOF) { + [_inputStreamReader unconsumeCurrentInputCharacter]; + } } - (void)HTMLTokenizerStateMarkupDeclarationOpen { - + if ([_inputStreamReader consumeString:@"--" caseSensitive:YES]) { + _currentCommentToken = [[HTMLCommentToken alloc] initWithData:@""]; + [self switchToState:HTMLTokenizerStateCommentStart]; + } else if ([_inputStreamReader consumeString:@"DOCTYPE" caseSensitive:NO]) { + [self switchToState:HTMLTokenizerStateDOCTYPE]; + } else if ([_inputStreamReader consumeString:@"[CDATA[" caseSensitive:YES]) { +#warning Implement HTML Elements and check for namespace in the _parser.adjustedCurrentNode.namespace + [self switchToState:HTMLTokenizerStateCDATASection]; + } else { + [self emitParseError:@"%@ unexpected/invalid character in Markup Declaration Open state", StringFromUTF32Char([_inputStreamReader nextInputCharacter])]; + [self switchToState:HTMLTokenizerStateBogusComment]; + } } - (void)HTMLTokenizerStateCommentStart { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case HYPHEN_MINUS: + [self switchToState:HTMLTokenizerStateCommentStartDash]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Comment Start state"]; + [_currentCommentToken appendCharacterToData:REPLACEMENT_CHAR]; + [self switchToState:HTMLTokenizerStateComment]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpeted > character in Comment Start state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in Comment Start state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentCommentToken appendCharacterToData:character]; + [self switchToState:HTMLTokenizerStateComment]; + break; + } } - (void)HTMLTokenizerStateCommentStartDash { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case HYPHEN_MINUS: + [self switchToState:HTMLTokenizerStateCommentEnd]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Comment Start Dash state"]; + [_currentCommentToken appendCharacterToData:HYPHEN_MINUS]; + [_currentCommentToken appendCharacterToData:REPLACEMENT_CHAR]; + [self switchToState:HTMLTokenizerStateComment]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpeted > character in Comment Start Dash state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in Comment Start Dash state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentCommentToken appendCharacterToData:HYPHEN_MINUS]; + [_currentCommentToken appendCharacterToData:character]; + [self switchToState:HTMLTokenizerStateComment]; + break; + } } - (void)HTMLTokenizerStateComment { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case HYPHEN_MINUS: + [self switchToState:HTMLTokenizerStateCommentEndDash]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Comment state"]; + [_currentCommentToken appendCharacterToData:REPLACEMENT_CHAR]; + break; + case EOF: + [self emitParseError:@"EOF reached in Comment state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentCommentToken appendCharacterToData:character]; + break; + } } - (void)HTMLTokenizerStateCommentEndDash { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case HYPHEN_MINUS: + [self switchToState:HTMLTokenizerStateCommentEnd]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Comment End Dash state"]; + [_currentCommentToken appendStringToData:@"-\uFFFD"]; + [self switchToState:HTMLTokenizerStateComment]; + break; + case EOF: + [self emitParseError:@"EOF reached in Comment End Dash state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentCommentToken appendCharacterToData:HYPHEN_MINUS]; + [_currentCommentToken appendCharacterToData:character]; + [self switchToState:HTMLTokenizerStateComment]; + break; + } } - (void)HTMLTokenizerStateCommentEnd { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Comment End state"]; + [_currentCommentToken appendStringToData:@"--\uFFFD"]; + [self switchToState:HTMLTokenizerStateComment]; + break; + case EXCLAMATION_MARK: + [self emitParseError:@"0x0021 unexpected ! in Comment End state"]; + [self switchToState:HTMLTokenizerStateCommentEndBang]; + break; + case HYPHEN_MINUS: + [self emitParseError:@"0x002D unexpected - in Comment End state"]; + [_currentCommentToken appendCharacterToData:HYPHEN_MINUS]; + break; + case EOF: + [self emitParseError:@"EOF reached in Comment End state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in Comment End state", StringFromUTF32Char(character)]; + [_currentCommentToken appendStringToData:@"--"]; + [_currentCommentToken appendCharacterToData:character]; + [self switchToState:HTMLTokenizerStateComment]; + break; + } } - (void)HTMLTokenizerStateCommentEndBang { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case HYPHEN_MINUS: + [_currentCommentToken appendStringToData:@"--!"]; + [self switchToState:HTMLTokenizerStateCommentEndDash]; + break; + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Comment End Bang state"]; + [_currentCommentToken appendStringToData:@"--!\uFFFD"]; + [self switchToState:HTMLTokenizerStateComment]; + break; + case EOF: + [self emitParseError:@"EOF reached in Comment End Bang state"]; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentCommentToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in Comment End state", StringFromUTF32Char(character)]; + [_currentCommentToken appendStringToData:@"--!"]; + [_currentCommentToken appendCharacterToData:character]; + [self switchToState:HTMLTokenizerStateComment]; + break; + } } - (void)HTMLTokenizerStateDOCTYPE { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + [self switchToState:HTMLTokenizerStateBeforeDOCTYPEName]; + break; + case EOF: + [self emitParseError:@"EOF reached in DOCTYPE state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken = [HTMLDOCTYPEToken new]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in DOCTYPE state", StringFromUTF32Char(character)]; + [self switchToState:HTMLTokenizerStateBeforeDOCTYPEName]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + } } - (void)HTMLTokenizerStateBeforeDOCTYPEName { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + break; + case LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z: + _currentDoctypeToken = [[HTMLDOCTYPEToken alloc] initWithName:StringFromUniChar(character + 0x0020)]; + [self switchToState:HTMLTokenizerStateDOCTYPEName]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in Before DOCTYPE Name state"]; + _currentDoctypeToken = [[HTMLDOCTYPEToken alloc] initWithName:StringFromUniChar(REPLACEMENT_CHAR)]; + [self switchToState:HTMLTokenizerStateDOCTYPEName]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in Before DOCTYPE Name state"]; + _currentDoctypeToken = [HTMLDOCTYPEToken new]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in Before DOCTYPE Name state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken = [HTMLDOCTYPEToken new]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + _currentDoctypeToken = [[HTMLDOCTYPEToken alloc] initWithName:StringFromUTF32Char(character)]; + [self switchToState:HTMLTokenizerStateDOCTYPEName]; + break; + } } - (void)HTMLTokenizerStateDOCTYPEName { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + [self switchToState:HTMLTokenizerStateAfterDOCTYPEName]; + break; + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z: + [_currentDoctypeToken appendCharacterToName:(character + 0x0020)]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in DOCTYPE Name state"]; + [_currentDoctypeToken appendCharacterToName:REPLACEMENT_CHAR]; + break; + case EOF: + [self emitParseError:@"EOF reached in DOCTYPE Name state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentDoctypeToken appendCharacterToName:character]; + break; + } } - (void)HTMLTokenizerStateAfterDOCTYPEName { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + break; + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in After DOCTYPE Name state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + { + [_inputStreamReader unconsumeCurrentInputCharacter]; + if ([_inputStreamReader consumeString:@"PUBLIC" caseSensitive:NO]) { + [self switchToState:HTMLTokenizerStateAfterDOCTYPEPublicKeyword]; + } else if ([_inputStreamReader consumeString:@"SYSTEM" caseSensitive:NO]) { + [self switchToState:HTMLTokenizerStateAfterDOCTYPESystemKeyword]; + } else { + [_inputStreamReader consumeNextInputCharacter]; + [self emitParseError:@"%@ unexpected character in After DOCTYPE Name state", StringFromUTF32Char(character)]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateBogusDOCTYPE]; + } + break; + } + } } - (void)HTMLTokenizerStateAfterDOCTYPEPublicKeyword { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + [self switchToState:HTMLTokenizerStateBeforeDOCTYPEPublicIdentifier]; + break; + case QUOTATION_MARK: + [self emitParseError:@"0x0022 unexpected \" character in After DOCTYPE Public Keyword state"]; + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPEPublicIdentifierDoubleQuoted]; + break; + case APOSTROPHE: + [self emitParseError:@"0x0027 unexpected ' character in After DOCTYPE Public Keyword state"]; + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPEPublicIdentifierSingleQuoted]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in After DOCTYPE Public Keyword state"]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in After DOCTYPE Public Keyword state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in After DOCTYPE Public Keyword state", StringFromUTF32Char(character)]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateBogusDOCTYPE]; + break; + } } - (void)HTMLTokenizerStateBeforeDOCTYPEPublicIdentifier { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + break; + case QUOTATION_MARK: + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPEPublicIdentifierDoubleQuoted]; + break; + case APOSTROPHE: + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPEPublicIdentifierSingleQuoted]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in Before DOCTYPE Public Identifier state"]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in After DOCTYPE Public Identifier state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in After DOCTYPE Public Identifier state", StringFromUTF32Char(character)]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateBogusDOCTYPE]; + break; + } } - (void)HTMLTokenizerStateDOCTYPEPublicIdentifierDoubleQuoted { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case QUOTATION_MARK: + [self switchToState:HTMLTokenizerStateAfterDOCTYPEPublicIdentifier]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in DOCTYPE Public Identifier Double-Quoted state"]; + [_currentDoctypeToken.publicIdentifier appendString:StringFromUniChar(REPLACEMENT_CHAR)]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in DOCTYPE Public Identifier Double-Quoted state"]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in DOCTYPE Public Identifier Double-Quoted state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentDoctypeToken.publicIdentifier appendString:StringFromUTF32Char(character)]; + break; + } } - (void)HTMLTokenizerStateDOCTYPEPublicIdentifierSingleQuoted { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case APOSTROPHE: + [self switchToState:HTMLTokenizerStateAfterDOCTYPEPublicIdentifier]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in DOCTYPE Public Identifier Single-Quoted state"]; + [_currentDoctypeToken.publicIdentifier appendString:StringFromUniChar(REPLACEMENT_CHAR)]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in DOCTYPE Public Identifier Single-Quoted state"]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in DOCTYPE Public Identifier Single-Quoted state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentDoctypeToken.publicIdentifier appendString:StringFromUTF32Char(character)]; + break; + } } - (void)HTMLTokenizerStateAfterDOCTYPEPublicIdentifier { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + [self switchToState:HTMLTokenizerStateBetweenDOCTYPEPublicAndSystemIdentifiers]; + break; + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case QUOTATION_MARK: + [self emitParseError:@"0x0022 unexpected \" character in After DOCTYPE Public Identifier state"]; + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPESystemIdentifierDoubleQuoted]; + break; + case APOSTROPHE: + [self emitParseError:@"0x0027 unexpected ' character in After DOCTYPE Public Identifier state"]; + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPESystemIdentifierSingleQuoted]; + break; + case EOF: + [self emitParseError:@"EOF reached in After DOCTYPE Public Identifier state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in After DOCTYPE Public Identifier state", StringFromUTF32Char(character)]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateBogusDOCTYPE]; + break; + } } - (void)HTMLTokenizerStateBetweenDOCTYPEPublicAndSystemIdentifiers { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + break; + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case QUOTATION_MARK: + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPESystemIdentifierDoubleQuoted]; + break; + case APOSTROPHE: + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPESystemIdentifierSingleQuoted]; + break; + case EOF: + [self emitParseError:@"EOF reached in Between DOCTYPE Public And System Identifiers state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in Between DOCTYPE Public And System Identifiers state", StringFromUTF32Char(character)]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateBogusDOCTYPE]; + break; + } } - (void)HTMLTokenizerStateAfterDOCTYPESystemKeyword { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + [self switchToState:HTMLTokenizerStateBeforeDOCTYPESystemIdentifier]; + break; + case QUOTATION_MARK: + [self emitParseError:@"0x0022 unexpected \" character in After DOCTYPE System Keyword state"]; + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPESystemIdentifierDoubleQuoted]; + break; + case APOSTROPHE: + [self emitParseError:@"0x0027 unexpected ' character in After DOCTYPE System Keyword state"]; + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPESystemIdentifierSingleQuoted]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in After DOCTYPE System Keyword state"]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in After DOCTYPE System Keyword state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in After DOCTYPE System Keyword state", StringFromUTF32Char(character)]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateBogusDOCTYPE]; + break; + } } - (void)HTMLTokenizerStateBeforeDOCTYPESystemIdentifier { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + break; + case QUOTATION_MARK: + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPESystemIdentifierDoubleQuoted]; + break; + case APOSTROPHE: + [_currentDoctypeToken.publicIdentifier setString:@""]; + [self switchToState:HTMLTokenizerStateDOCTYPESystemIdentifierSingleQuoted]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in Before DOCTYPE System Identifier state"]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in Before DOCTYPE System Identifier state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in Before DOCTYPE System Identifier state", StringFromUTF32Char(character)]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateBogusDOCTYPE]; + break; + } } - (void)HTMLTokenizerStateDOCTYPESystemIdentifierDoubleQuoted { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case QUOTATION_MARK: + [self switchToState:HTMLTokenizerStateAfterDOCTYPESystemIdentifier]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in DOCTYPE System Identifier Double-Quoted state"]; + [_currentDoctypeToken.systemIdentifier appendString:StringFromUniChar(REPLACEMENT_CHAR)]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in Before DOCTYPE System Identifier Double-Quoted state"]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in Before DOCTYPE System Identifier Double-Quoted state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentDoctypeToken.systemIdentifier appendString:StringFromUTF32Char(character)]; + break; + } } - (void)HTMLTokenizerStateDOCTYPESystemIdentifierSingleQuoted { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case APOSTROPHE: + [self switchToState:HTMLTokenizerStateAfterDOCTYPESystemIdentifier]; + break; + case NULL_CHAR: + [self emitParseError:@"0x0000 NULL character in DOCTYPE System Identifier Single-Quoted state"]; + [_currentDoctypeToken.systemIdentifier appendString:StringFromUniChar(REPLACEMENT_CHAR)]; + break; + case GREATER_THAN_SIGN: + [self emitParseError:@"0x003E unexpected > character in Before DOCTYPE System Identifier Single-Quoted state"]; + _currentDoctypeToken.forceQuirks = YES; + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in Before DOCTYPE System Identifier Single-Quoted state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [_currentDoctypeToken.systemIdentifier appendString:StringFromUTF32Char(character)]; + break; + } } - (void)HTMLTokenizerStateAfterDOCTYPESystemIdentifier { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case CHARACTER_TABULATION: + case LINE_FEED: + case FORM_FEED: + case SPACE: + break; + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self emitParseError:@"EOF reached in After DOCTYPE System Identifier state"]; + [self switchToState:HTMLTokenizerStateData]; + _currentDoctypeToken.forceQuirks = YES; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + [self emitParseError:@"%@ unexpected character in After DOCTYPE System Identifier state", StringFromUTF32Char(character)]; + [self switchToState:HTMLTokenizerStateBogusDOCTYPE]; + break; + } } - (void)HTMLTokenizerStateBogusDOCTYPE { - + UTF32Char character = [_inputStreamReader consumeNextInputCharacter]; + switch (character) { + case GREATER_THAN_SIGN: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + break; + case EOF: + [self switchToState:HTMLTokenizerStateData]; + [self emitToken:_currentDoctypeToken]; + [_inputStreamReader unconsumeCurrentInputCharacter]; + break; + default: + break; + } } - (void)HTMLTokenizerStateCDATASection { - + [self switchToState:HTMLTokenizerStateData]; + + NSString *characters = [_inputStreamReader consumeCharactersUpToString:@"]]>"]; + [self emitCharacterTokenWithString:characters]; + +#warning Check if necessary + if ([_inputStreamReader consumeNextInputCharacter] == (UTF32Char)EOF) { + [_inputStreamReader unconsumeCurrentInputCharacter]; + } } @end diff --git a/HTMLKit/HTMLTokenizerCharacters.h b/HTMLKit/HTMLTokenizerCharacters.h index 184cd41..5b5911b 100644 --- a/HTMLKit/HTMLTokenizerCharacters.h +++ b/HTMLKit/HTMLTokenizerCharacters.h @@ -14,8 +14,10 @@ CHAR( CARRIAGE_RETURN, 0x000D ) \ CHAR( SPACE, 0x0020 ) \ CHAR( EXCLAMATION_MARK, 0x0021 ) \ + CHAR( QUOTATION_MARK, 0x0022 ) \ CHAR( NUMBER_SIGN, 0x0023 ) \ CHAR( AMPERSAND, 0x0026 ) \ + CHAR( APOSTROPHE, 0x0027 ) \ CHAR( SOLIDUS, 0x002F ) \ CHAR( DIGIT_ZERO, 0x0030 ) \ CHAR( DIGIT_NINE, 0x0039 ) \ @@ -23,12 +25,16 @@ CHAR( LATIN_CAPITAL_LETTER_F, 0x0046 ) \ CHAR( LATIN_CAPITAL_LETTER_X, 0x0058 ) \ CHAR( LATIN_CAPITAL_LETTER_Z, 0x005A ) \ + CHAR( GRAVE_ACCENT, 0x0060 ) \ CHAR( LATIN_SMALL_LETTER_A, 0x0061 ) \ CHAR( LATIN_SMALL_LETTER_F, 0x0066 ) \ CHAR( LATIN_SMALL_LETTER_X, 0x0078 ) \ CHAR( LATIN_SMALL_LETTER_Z, 0x007A ) \ + CHAR( HYPHEN_MINUS, 0x002D ) \ CHAR( SEMICOLON, 0x003B ) \ CHAR( LESS_THAN_SIGN, 0x003C ) \ + CHAR( EQUALS_SIGN, 0x003D ) \ + CHAR( GREATER_THAN_SIGN, 0x003E ) \ CHAR( QUESTION_MARK, 0x003F ) \ CHAR( REPLACEMENT_CHAR, 0xFFFD ) @@ -93,8 +99,8 @@ NS_INLINE BOOL isHexDigit(UTF32Char character) NS_INLINE BOOL isValidNumericRange(UTF32Char character) { - return ((character >= 0xD800 && character <= 0xDFFF) - || character > 0x10FFFF); + return ((character >= 0xD800 && character <= 0xDFFF) || + character > 0x10FFFF); } NS_INLINE unichar NumericReplacementCharacter(UTF32Char character)