Add implementation for handling end tag tokens in "in body" insertion mode

https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
This commit is contained in:
iska
2015-03-11 22:47:42 +01:00
parent 80f04cb7df
commit 56e6cd5650
3 changed files with 168 additions and 19 deletions
+145 -3
View File
@@ -964,6 +964,9 @@
case HTMLTokenTypeStartTag:
[self processStartTagTokenInBody:token.asStartTagToken];
return;
case HTMLTokenTypeEndTag:
[self processEndTagTokenInBody:token.asEndTagToken];
return;
default:
break;
}
@@ -1114,7 +1117,7 @@
if (element != nil) {
[self emitParseError:@"Unexpected nested Start Tag (a) in <body>"];
if ([self runAdoptionAgencyAlgorithmForTagName:@"a"]) {
#warning Handle Any Other End Tag Token
[self processAnyOtherEndTagTokenInBody:token];
return;
}
[_listOfActiveFormattingElements removeObject:element];
@@ -1133,7 +1136,7 @@
if ([_stackOfOpenElements hasElementInScopeWithTagName:@"nobr"]) {
[self emitParseError:@"Unexpected nested Start Tag (nobr) in <body>"];
if ([self runAdoptionAgencyAlgorithmForTagName:@"nobr"]) {
#warning Handle Any Other End Tag Token
[self processAnyOtherEndTagTokenInBody:token];
return;
}
[self reconstructActiveFormattingElements];
@@ -1142,7 +1145,7 @@
} else if ([token.tagName isEqualToAny:@"a", @"b", @"big", @"code", @"em", @"font", @"i", @"nobr",
@"s", @"small", @"strike", @"strong", @"tt", @"u", nil]) {
if ([self runAdoptionAgencyAlgorithmForTagName:tagName]) {
#warning Handle Any Other End Tag Token
[self processAnyOtherEndTagTokenInBody:token];
return;
}
} else if ([tagName isEqualToAny:@"applet", @"marquee", nil]) {
@@ -1299,6 +1302,145 @@
}
}
- (void)processEndTagTokenInBody:(HTMLEndTagToken *)token
{
NSString *tagName = token.tagName;
if ([tagName isEqualToString:@"template"]) {
#warning Implement HTML Template
} else if ([tagName isEqualToAny:@"body", @"html", nil]) {
// End tags "body" & "html" are identical, expect for the reprocessing step
if (![_stackOfOpenElements hasElementInScopeWithTagName:@"body"]) {
[self emitParseError:@"End Tag (body) without body element in scope in <body>"];
}
for (HTMLElement *node in _stackOfOpenElements) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"optgroup", @"option", @"p", @"rp"
@"rt", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
[self emitParseError:@"End Tag (%@) with open element (%@) in <body>", tagName, node.tagName];
break;
}
}
[self switchInsertionMode:HTMLInsertionModeAfterBody];
if ([tagName isEqualToString:@"html"]) {
[self reprocessToken:token];
}
} else if ([tagName isEqualToAny:@"address", @"article", @"aside", @"blockquote", @"button",
@"center", @"details", @"dialog", @"dir", @"div", @"dl", @"fieldset", @"figcaption",
@"figure", @"footer", @"header", @"hgroup", @"listing", @"main", @"menu", @"nav",
@"ol", @"pre", @"section", @"summary", @"ul", nil]) {
if ([_stackOfOpenElements hasElementInScopeWithTagName:tagName]) {
[self emitParseError:@"End Tag (%@) with open element in <body>", tagName];
return;
}
[self generateImpliedEndTagsExceptForElement:nil];
if (![self.currentNode.tagName isEqualToString:tagName]) {
[self emitParseError:@"Unexpected End Tag Token (%@) in <body>", tagName];
}
[_stackOfOpenElements popElementsUntilElementPoppedWithTagName:tagName];
} else if ([tagName isEqualToString:@"form"]) {
#warning Implement HTML Template
HTMLElement *node = _formElementPointer;
_formElementPointer = nil;
if (node == nil || ![_stackOfOpenElements hasElementInScopeWithTagName:node.tagName]) {
[self emitParseError:@"Unexpected closed (form) element in <body>"];
return;
}
[self generateImpliedEndTagsExceptForElement:nil];
if ([self.currentNode isEqual:node]) {
[self emitParseError:@"Unexpected nested (form) element in <body>"];
}
[_stackOfOpenElements removeElement:node];
} else if ([tagName isEqualToString:@"p"]) {
if (![_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self emitParseError:@"Unexpected End Tag Token (p) in <body>"];
HTMLEndTagToken *pToken = [[HTMLEndTagToken alloc] initWithTagName:@"p"];
[self insertElementForToken:pToken];
}
[self closePElement];
} else if ([tagName isEqualToString:@"li"]) {
if (![_stackOfOpenElements hasElementInScopeWithTagName:@"li"]) {
[self emitParseError:@"Unexpected closed (li) element in <body>"];
return;
}
[self generateImpliedEndTagsExceptForElement:nil];
if ([self.currentNode.tagName isEqualToString:@"li"]) {
[self emitParseError:@"Unexpected nested (li) element in <body>"];
}
[_stackOfOpenElements popElementsUntilElementPoppedWithTagName:@"li"];
} else if ([tagName isEqualToAny:@"dd", @"dt", nil]) {
if (![_stackOfOpenElements hasElementInScopeWithTagName:tagName]) {
[self emitParseError:@"Unexpected closed (%@) element in <body>", tagName];
return;
}
[self generateImpliedEndTagsExceptForElement:tagName];
if ([self.currentNode.tagName isEqualToString:@"li"]) {
[self emitParseError:@"Unexpected nested (%@) element in <body>", tagName];
}
[_stackOfOpenElements popElementsUntilElementPoppedWithTagName:tagName];
} else if ([tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
if (![_stackOfOpenElements hasAnyElementInScopeWithAnyOfTagNames:@[@"h1", @"h2", @"h3", @"h4", @"h5", @"h6"]]) {
[self emitParseError:@"Unexpected closed (%@) element in <body>", tagName];
return;
}
[self generateImpliedEndTagsExceptForElement:nil];
if (![self.currentNode.tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
[self emitParseError:@"Unexpected nested (%@) element in <body>", tagName];
}
[_stackOfOpenElements popElementsUntilAnElementPoppedWithAnyOfTagNames:@[@"h1", @"h2", @"h3", @"h4", @"h5", @"h6"]];
} else if ([tagName isEqualToString:@"sarcasm"]) {
// Taking a Deep Breath
[self processAnyOtherEndTagTokenInBody:token];
return;
} else if ([tagName isEqualToAny:@"a", @"b", @"big", @"code", @"em", @"font", @"i", @"nobr", @"s", @"small", @"strike",
@"strong", @"tt", @"u", nil]) {
if ([self runAdoptionAgencyAlgorithmForTagName:tagName]) {
[self processAnyOtherEndTagTokenInBody:token];
return;
}
} else if ([tagName isEqualToAny:@"applet", @"marquee", @"object", nil]) {
if (![_stackOfOpenElements hasAnyElementInScopeWithAnyOfTagNames:@[@"applet", @"marquee", @"object"]]) {
[self emitParseError:@"Unexpected closed (%@) element in <body>", tagName];
return;
}
[self generateImpliedEndTagsExceptForElement:nil];
if (![self.currentNode.tagName isEqualToAny:@"applet", @"marquee", @"object", nil]) {
[self emitParseError:@"Unexpected nested (%@) element in <body>", tagName];
}
[_stackOfOpenElements popElementsUntilAnElementPoppedWithAnyOfTagNames:@[@"applet", @"marquee", @"object"]];
while (![_listOfActiveFormattingElements.lastObject isEqual:[HTMLMarker marker]]) {
[_listOfActiveFormattingElements removeLastObject];
}
[_listOfActiveFormattingElements removeLastObject];
} else if ([tagName isEqualToString:@"br"]) {
[self emitParseError:@"Unexpected End Tag Token (br) in <body>"];
HTMLStartTagToken *brToken = [[HTMLStartTagToken alloc] initWithTagName:@"br"];
[self processStartTagTokenInBody:brToken];
} else {
[self processAnyOtherEndTagTokenInBody:token];
}
}
- (void)processAnyOtherEndTagTokenInBody:(HTMLEndTagToken *)token
{
HTMLElement *node = _stackOfOpenElements.currentNode;
NSUInteger index = _stackOfOpenElements.count - 1;
while (YES) {
if ([node.tagName isEqualToString:token.tagName]) {
[self generateImpliedEndTagsExceptForElement:token.tagName];
if (![node.tagName isEqualToString:self.currentNode.tagName]) {
[self emitParseError:@"Unexpected nested End Tag Token (%@) in <body>", node.tagName];
}
[_stackOfOpenElements popElementsUntilElementPopped:node];
break;
} else if (IsSpecialElement(node)) {
[self emitParseError:@"Unexpected End Tag Token (%@) in <body>", node.tagName];
return;
}
node = _stackOfOpenElements[--index];
}
}
- (void)HTMLInsertionModeText:(HTMLToken *)token
{
+2
View File
@@ -28,11 +28,13 @@
- (void)popCurrentNode;
- (void)popElementsUntilElementPoppedWithTagName:(NSString *)tagName;
- (void)popElementsUntilAnElementPoppedWithAnyOfTagNames:(NSArray *)tagNames;
- (void)popElementsUntilElementPopped:(HTMLElement *)element;
- (void)popAll;
- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames;
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName;
+21 -16
View File
@@ -120,6 +120,14 @@
[_stack removeLastObject];
}
- (void)popElementsUntilAnElementPoppedWithAnyOfTagNames:(NSArray *)tagNames
{
while (![tagNames containsObject:self.currentNode.tagName]) {
[_stack removeLastObject];
}
[_stack removeLastObject];
}
- (void)popElementsUntilElementPopped:(HTMLElement *)element
{
while (![self.currentNode isEqual:element]) {
@@ -137,7 +145,12 @@
- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
{
return [self hasElementInSpecificScopeWithTagName:tagName andElementTypes:_specificScopeElementTypes];
return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:_specificScopeElementTypes];
}
- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames
{
return [self hasAnyElementInSpecificScopeWithTagNames:tagNames andElementTypes:_specificScopeElementTypes];
}
- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName
@@ -167,25 +180,17 @@
@"template": @(HTMLNamespaceHTML)}];
}
- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if ([node.tagName isEqualToString:tagName]) {
return node;
}
if (!(node.namespace == HTMLNamespaceHTML &&
[node.tagName isEqualToAny:@"optgroup", @"option", nil])) {
return nil;
}
}
return nil;
}
- (HTMLElement *)hasElementInSpecificScopeWithTagName:(NSString *)tagName
andElementTypes:(NSDictionary *)elementTypes
{
return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:elementTypes];
}
- (HTMLElement *)hasAnyElementInSpecificScopeWithTagNames:(NSArray *)tagNames
andElementTypes:(NSDictionary *)elementTypes
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if ([node.tagName isEqualToString:tagName]) {
if ([tagNames containsObject:node.tagName]) {
return node;
}
if ([elementTypes[node.tagName] isEqual:@(node.namespace)]) {