diff --git a/Sources/HTMLParser.m b/Sources/HTMLParser.m
index 3137081..2485650 100644
--- a/Sources/HTMLParser.m
+++ b/Sources/HTMLParser.m
@@ -1529,7 +1529,7 @@
}
[self closePElement];
} else if ([tagName isEqualToString:@"li"]) {
- if (![_stackOfOpenElements hasElementInListItemScopeWithTagName:@"li"]) {
+ if (![_stackOfOpenElements hasElementInListItemScopeWithTagName:tagName]) {
[self emitParseError:@"Unexpected
element in "];
return;
}
@@ -1549,7 +1549,7 @@
}
[_stackOfOpenElements popElementsUntilElementPoppedWithTagName:tagName];
} else if ([tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
- if (![_stackOfOpenElements hasAnyElementInScopeWithAnyOfTagNames:@[@"h1", @"h2", @"h3", @"h4", @"h5", @"h6"]]) {
+ if (![_stackOfOpenElements hasHeaderElementInScope]) {
[self emitParseError:@"Unexpected <%@> element in ", tagName];
return;
}
@@ -1569,7 +1569,7 @@
return;
}
} else if ([tagName isEqualToAny:@"applet", @"marquee", @"object", nil]) {
- if (![_stackOfOpenElements hasAnyElementInScopeWithAnyOfTagNames:@[@"applet", @"marquee", @"object"]]) {
+ if (![_stackOfOpenElements hasElementInScopeWithTagName:tagName]) {
[self emitParseError:@"Unexpected <%@> element in ", tagName];
return;
}
diff --git a/Sources/HTMLStackOfOpenElements.m b/Sources/HTMLStackOfOpenElements.m
index daf1517..7420537 100644
--- a/Sources/HTMLStackOfOpenElements.m
+++ b/Sources/HTMLStackOfOpenElements.m
@@ -14,7 +14,6 @@
@interface HTMLStackOfOpenElements ()
{
NSMutableArray *_stack;
- NSDictionary *_specificScopeElementTypes;
}
@end
@@ -27,26 +26,6 @@
self = [super init];
if (self) {
_stack = [NSMutableArray new];
- _specificScopeElementTypes = @{
- @"applet": @(HTMLNamespaceHTML),
- @"caption": @(HTMLNamespaceHTML),
- @"html": @(HTMLNamespaceHTML),
- @"table": @(HTMLNamespaceHTML),
- @"td": @(HTMLNamespaceHTML),
- @"th": @(HTMLNamespaceHTML),
- @"marquee": @(HTMLNamespaceHTML),
- @"object": @(HTMLNamespaceHTML),
- @"template": @(HTMLNamespaceHTML),
- @"mi": @(HTMLNamespaceMathML),
- @"mo": @(HTMLNamespaceMathML),
- @"mn": @(HTMLNamespaceMathML),
- @"ms": @(HTMLNamespaceMathML),
- @"mtext": @(HTMLNamespaceMathML),
- @"annotation-xml": @(HTMLNamespaceMathML),
- @"foreignObject": @(HTMLNamespaceSVG),
- @"desc": @(HTMLNamespaceSVG),
- @"title": @(HTMLNamespaceSVG)
- };
}
return self;
}
@@ -195,82 +174,148 @@
#pragma mark - Element Scope
+NS_INLINE BOOL IsSpecificScopeElement(HTMLElement *element)
+{
+ switch (element.htmlNamespace) {
+ case HTMLNamespaceHTML:
+ return [element.tagName isEqualToAny:@"applet", @"caption", @"html", @"table", @"td", @"th", @"marquee", @"object", @"template", nil];
+ case HTMLNamespaceMathML:
+ return [element.tagName isEqualToAny:@"mi", @"mo", @"mn", @"ms", @"mtext", @"annotation-xml", nil];
+ case HTMLNamespaceSVG:
+ return [element.tagName isEqualToAny:@"foreignObject", @"desc", @"title", nil];
+ }
+}
+
+NS_INLINE BOOL IsHeaderElement(HTMLElement *element)
+{
+ if (element.htmlNamespace != HTMLNamespaceHTML) {
+ return NO;
+ }
+
+ return [element.tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil];
+}
+
+NS_INLINE BOOL IsTableScopeElement(HTMLElement *element)
+{
+ if (element.htmlNamespace != HTMLNamespaceHTML) {
+ return NO;
+ }
+
+ return [element.tagName isEqualToAny:@"html", @"table", @"template", nil];
+}
+
+NS_INLINE BOOL IsListItemScopeElement(HTMLElement *element)
+{
+ if (element.htmlNamespace != HTMLNamespaceHTML) {
+ return NO;
+ }
+
+ return [element.tagName isEqualToAny:@"ol", @"ul", nil];
+}
+
+NS_INLINE BOOL IsSelectScopeElement(HTMLElement *element)
+{
+ if (element.htmlNamespace != HTMLNamespaceHTML) {
+ return NO;
+ }
+
+ return ![element.tagName isEqualToString:@"optgroup"] && ![element.tagName isEqualToString:@"option"];
+}
+
+NS_INLINE BOOL IsButtonScopeElement(HTMLElement *element)
+{
+ if (element.htmlNamespace != HTMLNamespaceHTML) {
+ return NO;
+ }
+
+ return [element.tagName isEqualToString:@"button"];
+}
+
- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
-{
- return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:_specificScopeElementTypes];
-}
-
-- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames
-{
- return [self hasAnyElementInSpecificScopeWithTagNames:tagNames andElementTypes:_specificScopeElementTypes];
-}
-
-- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName
-{
- NSMutableDictionary *elementTypes = [NSMutableDictionary dictionaryWithDictionary:_specificScopeElementTypes];
- [elementTypes addEntriesFromDictionary:@{@"ol": @(HTMLNamespaceHTML),
- @"ul": @(HTMLNamespaceHTML)}];
-
- return [self hasElementInSpecificScopeWithTagName:tagName
- andElementTypes:elementTypes];
-}
-
-- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName
-{
- NSMutableDictionary *elementTypes = [NSMutableDictionary dictionaryWithDictionary:_specificScopeElementTypes];
- [elementTypes addEntriesFromDictionary:@{@"button": @(HTMLNamespaceHTML)}];
-
- return [self hasElementInSpecificScopeWithTagName:tagName
- andElementTypes:elementTypes];
-}
-
-- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName
-{
- return [self hasElementInSpecificScopeWithTagName:tagName
- andElementTypes:@{@"html": @(HTMLNamespaceHTML),
- @"table": @(HTMLNamespaceHTML),
- @"template": @(HTMLNamespaceHTML)}];
-}
-
-- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames
-{
- return [self hasAnyElementInSpecificScopeWithTagNames:tagNames
- andElementTypes:@{@"html": @(HTMLNamespaceHTML),
- @"table": @(HTMLNamespaceHTML),
- @"template": @(HTMLNamespaceHTML)}];
-}
-
-- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
- if ([node.tagName isEqualToString:tagName]) {
+ if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
return node;
}
- if (!(node.htmlNamespace == HTMLNamespaceHTML &&
- [node.tagName isEqualToAny:@"optgroup", @"option", nil])) {
+ if (IsSpecificScopeElement(node)) {
return nil;
}
}
return nil;
}
-- (HTMLElement *)hasElementInSpecificScopeWithTagName:(NSString *)tagName
- andElementTypes:(NSDictionary *)elementTypes
-{
- return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:elementTypes];
-}
-
-- (HTMLElement *)hasAnyElementInSpecificScopeWithTagNames:(NSArray *)tagNames
- andElementTypes:(NSDictionary *)elementTypes
+- (HTMLElement *)hasHeaderElementInScope
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
- if ([tagNames containsObject:node.tagName]) {
- NSNumber *namespace = elementTypes[node.tagName] ?: @(HTMLNamespaceHTML);
- if ([namespace isEqual:@(node.htmlNamespace)]) {
- return node;
- }
+ if (IsHeaderElement(node)) {
+ return node;
}
- if ([elementTypes[node.tagName] isEqual:@(node.htmlNamespace)]) {
+ if (IsSpecificScopeElement(node)) {
+ return nil;
+ }
+ }
+ return nil;
+}
+
+- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName
+{
+ for (HTMLElement *node in _stack.reverseObjectEnumerator) {
+ if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
+ return node;
+ }
+ if (IsTableScopeElement(node)) {
+ return nil;
+ }
+ }
+ return nil;
+}
+
+- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames
+{
+ for (HTMLElement *node in _stack.reverseObjectEnumerator) {
+ if (node.htmlNamespace == HTMLNamespaceHTML && [tagNames containsObject:node.tagName]) {
+ return node;
+ }
+ if (IsTableScopeElement(node)) {
+ return nil;
+ }
+ }
+ return nil;
+}
+
+- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName
+{
+ for (HTMLElement *node in _stack.reverseObjectEnumerator) {
+ if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
+ return node;
+ }
+ if (IsSpecificScopeElement(node) || IsListItemScopeElement(node)) {
+ return nil;
+ }
+ }
+ return nil;
+}
+
+- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName
+{
+ for (HTMLElement *node in _stack.reverseObjectEnumerator) {
+ if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
+ return node;
+ }
+ if (IsSpecificScopeElement(node) || IsButtonScopeElement(node)) {
+ return nil;
+ }
+ }
+ return nil;
+}
+
+- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
+{
+ for (HTMLElement *node in _stack.reverseObjectEnumerator) {
+ if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
+ return node;
+ }
+ if (IsSelectScopeElement(node)) {
return nil;
}
}
diff --git a/Sources/include/HTMLStackOfOpenElements.h b/Sources/include/HTMLStackOfOpenElements.h
index eccae52..a7f0e0e 100644
--- a/Sources/include/HTMLStackOfOpenElements.h
+++ b/Sources/include/HTMLStackOfOpenElements.h
@@ -163,11 +163,11 @@
https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-the-specific-scope
*/
- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
-- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames;
-- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName;
-- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName;
+- (HTMLElement *)hasHeaderElementInScope;
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasElementInTableScopeWithAnyOfTagNames:(NSArray *)tagNames;
+- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName;
+- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName;
- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName;
/**