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:@""];
+ [_inputStreamReader unconsumeCurrentInputCharacter];
+ default:
+ [self emitParseError:@"%X Unexpected character in End Tag Open state", character];
+ [self switchToState:HTMLTokenizerStateBogusComment];
+ break;
+ }
}
- (void)HTMLTokenizerStateTagName
{
-
+ 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 LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z:
+ [_currentTagToken appendCharacterToTagName:(character + 0x0020)];
+ break;
+ case NULL_CHAR:
+ [self emitParseError:@"0x0000 NULL character in Tag Name state"];
+ [_currentTagToken appendCharacterToTagName:REPLACEMENT_CHAR];
+ break;
+ case EOF:
+ [self emitParseError:@"EOF reached in Tag Name state"];
+ [_inputStreamReader unconsumeCurrentInputCharacter];
+ break;
+ default:
+ [_currentTagToken appendCharacterToTagName:character];
+ break;
+ }
}
- (void)HTMLTokenizerStateRCDATALessThanSign
{
-
+ UTF32Char character = [_inputStreamReader consumeNextInputCharacter];
+ switch (character) {
+ case SOLIDUS:
+ _temporaryBuffer = [NSMutableString new];
+ [self switchToState:HTMLTokenizerStateRCDATAEndTagOpen];
+ break;
+ default:
+ [self switchToState:HTMLTokenizerStateRCDATA];
+ [self emitCharacterToken:LESS_THAN_SIGN];
+ [_inputStreamReader unconsumeCurrentInputCharacter];
+ break;
+ }
}
- (void)HTMLTokenizerStateRCDATAEndTagOpen
{
-
+ UTF32Char character = [_inputStreamReader consumeNextInputCharacter];
+ switch (character) {
+ case LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z:
+ _currentTagToken = [[HTMLEndTagToken alloc] initWithTagName:StringFromUniChar(character + 0x0020)];
+ [_temporaryBuffer appendString:StringFromUniChar(character)];
+ [self switchToState:HTMLTokenizerStateRCDATAEndTagName];
+ break;
+ case LATIN_SMALL_LETTER_A ... LATIN_SMALL_LETTER_Z:
+ _currentTagToken = [[HTMLEndTagToken alloc] initWithTagName:StringFromUniChar(character)];
+ [_temporaryBuffer appendString:StringFromUniChar(character)];
+ [self switchToState:HTMLTokenizerStateRCDATAEndTagName];
+ break;
+ default:
+ [self switchToState:HTMLTokenizerStateRCDATA];
+ [self emitCharacterTokenWithString:@""];
+ [_inputStreamReader unconsumeCurrentInputCharacter];
+ break;
+ }
}
- (void)HTMLTokenizerStateRCDATAEndTagName
{
+ UTF32Char character = [_inputStreamReader consumeNextInputCharacter];
+ switch (character) {
+ case CHARACTER_TABULATION:
+ case LINE_FEED:
+ case FORM_FEED:
+ case SPACE:
+ if ([self isCurrentEndTagTokenAppropriate]) {
+ [self switchToState:HTMLTokenizerStateBeforeAttributeName];
+ return;
+ }
+ break;
+ case SOLIDUS:
+ if ([self isCurrentEndTagTokenAppropriate]) {
+ [self switchToState:HTMLTokenizerStateSelfClosingStartTag];
+ return;
+ }
+ break;
+ case GREATER_THAN_SIGN:
+ if ([self isCurrentEndTagTokenAppropriate]) {
+ [self switchToState:HTMLTokenizerStateData];
+ [self emitCurrentTagToken];
+ return;
+ }
+ break;
+ case LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z:
+ [_currentTagToken appendCharacterToTagName:(character + 0x0020)];
+ [_temporaryBuffer appendString:StringFromUniChar(character)];
+ return;
+ case LATIN_SMALL_LETTER_A ... LATIN_SMALL_LETTER_Z:
+ [_currentTagToken appendCharacterToTagName:character];
+ [_temporaryBuffer appendString:StringFromUniChar(character)];
+ return;
+ }
+ [self switchToState:HTMLTokenizerStateRCDATA];
+ [self emitCharacterTokenWithString:@""];
+ [self emitCharacterTokenWithString:_temporaryBuffer];
+ [_inputStreamReader unconsumeCurrentInputCharacter];
}
- (void)HTMLTokenizerStateRAWTEXTLessThanSign
{
-
+ UTF32Char character = [_inputStreamReader consumeNextInputCharacter];
+ switch (character) {
+ case SOLIDUS:
+ _temporaryBuffer = [NSMutableString new];
+ [self switchToState:HTMLTokenizerStateRAWTEXTEndTagOpen];
+ break;
+ default:
+ [self switchToState:HTMLTokenizerStateRAWTEXT];
+ [self emitCharacterToken:LESS_THAN_SIGN];
+ [_inputStreamReader unconsumeCurrentInputCharacter];
+ break;
+ }
}
- (void)HTMLTokenizerStateRAWTEXTEndTagOpen
{
-
+ UTF32Char character = [_inputStreamReader consumeNextInputCharacter];
+ switch (character) {
+ case LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z:
+ _currentTagToken = [[HTMLEndTagToken alloc] initWithTagName:StringFromUniChar(character + 0x0020)];
+ [_temporaryBuffer appendString:StringFromUniChar(character)];
+ [self switchToState:HTMLTokenizerStateRAWTEXTEndTagName];
+ break;
+ case LATIN_SMALL_LETTER_A ... LATIN_SMALL_LETTER_Z:
+ _currentTagToken = [[HTMLEndTagToken alloc] initWithTagName:StringFromUniChar(character)];
+ [_temporaryBuffer appendString:StringFromUniChar(character)];
+ [self switchToState:HTMLTokenizerStateRCDATAEndTagName];
+ break;
+ default:
+ [self switchToState:HTMLTokenizerStateRAWTEXT];
+ [self emitCharacterTokenWithString:@""];
+ [_inputStreamReader unconsumeCurrentInputCharacter];
+ break;
+ }
}
- (void)HTMLTokenizerStateRAWTEXTEndTagName
{
+ UTF32Char character = [_inputStreamReader consumeNextInputCharacter];
+ switch (character) {
+ case CHARACTER_TABULATION:
+ case LINE_FEED:
+ case FORM_FEED:
+ case SPACE:
+ if ([self isCurrentEndTagTokenAppropriate]) {
+ [self switchToState:HTMLTokenizerStateBeforeAttributeName];
+ return;
+ }
+ break;
+ case SOLIDUS:
+ if ([self isCurrentEndTagTokenAppropriate]) {
+ [self switchToState:HTMLTokenizerStateSelfClosingStartTag];
+ return;
+ }
+ break;
+ case GREATER_THAN_SIGN:
+ if ([self isCurrentEndTagTokenAppropriate]) {
+ [self switchToState:HTMLTokenizerStateData];
+ [self emitCurrentTagToken];
+ return;
+ }
+ break;
+ case LATIN_CAPITAL_LETTER_A ... LATIN_CAPITAL_LETTER_Z:
+ [_currentTagToken appendCharacterToTagName:(character + 0x0020)];
+ [_temporaryBuffer appendString:StringFromUniChar(character)];
+ return;
+ case LATIN_SMALL_LETTER_A ... LATIN_SMALL_LETTER_Z:
+ [_currentTagToken appendCharacterToTagName:character];
+ [_temporaryBuffer appendString:StringFromUniChar(character)];
+ return;
+ }
+ [self switchToState:HTMLTokenizerStateRAWTEXT];
+ [self emitCharacterTokenWithString:@""];
+ [self emitCharacterTokenWithString:_temporaryBuffer];
+ [_inputStreamReader unconsumeCurrentInputCharacter];
}
- (void)HTMLTokenizerStateScriptDataLessThanSign
{
-
+ UTF32Char character = [_inputStreamReader consumeNextInputCharacter];
+ switch (character) {
+ case SOLIDUS:
+ _temporaryBuffer = [NSMutableString new];
+ [self switchToState:HTMLTokenizerStateScriptDataEndTagOpen];
+ break;
+ case EXCLAMATION_MARK:
+ [self switchToState:HTMLTokenizerStateScriptDataEscapeStart];
+ [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)