diff --git a/.jazzy.yaml b/.jazzy.yaml
index 4bcae51..c95515d 100644
--- a/.jazzy.yaml
+++ b/.jazzy.yaml
@@ -1,5 +1,5 @@
module: HTMLKit
-module_version: 2.0.5
+module_version: 2.0.6
author: Iskandar Abudiab
author_url: https://twitter.com/iabudiab
github_url: https://github.com/iabudiab/HTMLKit
diff --git a/.travis.yml b/.travis.yml
index 313ee3f..980d094 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
language: objective-c
-osx_image: xcode8.2
+osx_image: xcode8.3
branches:
except:
@@ -17,21 +17,19 @@ env:
- MACOS_FRAMEWORK_SCHEME=HTMLKit-macOS
- WATCHOS_FRAMEWORK_SCHEME="HTMLKit-watchOS"
- TVOS_FRAMEWORK_SCHEME="HTMLKit-tvOS"
- - IOS_SDK=iphonesimulator10.2
+ - IOS_SDK=iphonesimulator10.3
- MACOS_SDK=macosx10.12
- - WATCHOS_SDK=watchsimulator3.1
- - TVOS_SDK=appletvsimulator10.1
+ - WATCHOS_SDK=watchsimulator3.2
+ - TVOS_SDK=appletvsimulator10.2
matrix:
- DESTINATION="arch=x86_64" SIMULATOR="" SCHEME="$MACOS_FRAMEWORK_SCHEME" SDK="$MACOS_SDK"
- - DESTINATION="OS=9.0,name=iPhone 6" SIMULATOR="iPhone 6 (9.0)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- - DESTINATION="OS=9.1,name=iPhone 6 Plus" SIMULATOR="iPhone 6 Plus (9.1)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- - DESTINATION="OS=9.2,name=iPhone 6S" SIMULATOR="iPhone 6S (9.2)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
+ - DESTINATION="OS=8.4,name=iPhone 6 Plus" SIMULATOR="iPhone 6 Plus (8.4)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=9.3,name=iPhone 6S Plus" SIMULATOR="iPhone 6S Plus (9.3)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- - DESTINATION="OS=10.1,name=iPhone 7 Plus" SIMULATOR="iPhone 7 Plus (10.1)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
+ - DESTINATION="OS=10.3,name=iPhone 7 Plus" SIMULATOR="iPhone 7 Plus (10.3)" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK"
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SIMULATOR="Apple Watch - 42mm (2.2)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
- - DESTINATION="OS=3.1,name=Apple Watch Series 2 - 42mm" SIMULATOR="Apple Watch Series 2 - 42mm (3.1)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
- - DESTINATION="OS=9.0,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (9.2)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
- - DESTINATION="OS=10.0,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (10.0)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
+ - DESTINATION="OS=3.2,name=Apple Watch Series 2 - 42mm" SIMULATOR="Apple Watch Series 2 - 42mm (3.2)" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK"
+ - DESTINATION="OS=9.2,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (9.2)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
+ - DESTINATION="OS=10.2,name=Apple TV 1080p" SIMULATOR="Apple TV 1080p (10.2)" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK"
script:
- set -o pipefail
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f67a6c..dcf1f94 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,15 +1,30 @@
# Change Log
+## [2.0.6](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.6)
+
+Released on 2017.05.02
+
+### Added
+
+- Memory consumption improvements (issue #10)
+ - Allocate `childNodes` collection in `HTMLNode` only when inserting child nodes
+ - Replace `NSStringFromSelector` calls with constants in `HTMLNode` validations
+ - Improve `reverseObjectEnumerator` usage while parsing HTML
+ - Rewrite internal logic of the `HTMLStackOfOpenElements` to prevent excessive allocations
+
+
## [2.0.5](https://github.com/iabudiab/HTMLKit/releases/tag/2.0.5)
Released on 2017.04.19
### Fixed
+
- Xcode 8.3 issue with modulemaps
- Temporary workaround (renamed modulemap file)
- Memory Leaks in `CSSInputStream`
### Added
+
- Minor memory consumption improvements
- Collections for child nodes or attributes of HTML Nodes or Elements are allocated lazily
- Underyling data string of `CharacterData` is allocated on first access
@@ -21,10 +36,12 @@ Released on 2017.04.19
Released on 2017.04.2
### Fixed
+
- Testing with Swift 3.1
- Fixed by @tali in PR #8
### Deprecated
+
- `HTMLRange` initializers with typo
- `initWithDowcument:startContainer:startOffset:endContainer:endOffset:`
@@ -34,6 +51,7 @@ Released on 2017.04.2
Released on 2017.03.6
### Fixed
+
- Compilation for Swift 3.1
- Fixed by @tali in PR #6
@@ -43,6 +61,7 @@ Released on 2017.03.6
Released on 2017.02.26
### Fixed
+
- Retain cycles in `HTMLNodeIterator` (issue #4)
- Retain cycles in `HTMLRange` (issue #5)
- The layout of `HTMLKit` tests module for Swift Package Manager
diff --git a/HTMLKit.podspec b/HTMLKit.podspec
index 4991a29..50e830e 100644
--- a/HTMLKit.podspec
+++ b/HTMLKit.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "HTMLKit"
- s.version = "2.0.5"
+ s.version = "2.0.6"
s.summary = "HTMLKit, an Objective-C framework for your everyday HTML needs."
s.license = "MIT"
s.homepage = "https://github.com/iabudiab/HTMLKit"
diff --git a/Sources/HTMLKit-Info.plist b/Sources/HTMLKit-Info.plist
index 11b6ce9..7b6a588 100644
--- a/Sources/HTMLKit-Info.plist
+++ b/Sources/HTMLKit-Info.plist
@@ -17,7 +17,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.0.5
+ 2.0.6
CFBundleSignature
????
CFBundleVersion
diff --git a/Sources/HTMLListOfActiveFormattingElements.m b/Sources/HTMLListOfActiveFormattingElements.m
index d0fb8b0..665ce8f 100644
--- a/Sources/HTMLListOfActiveFormattingElements.m
+++ b/Sources/HTMLListOfActiveFormattingElements.m
@@ -46,7 +46,7 @@ - (NSUInteger)indexOfElement:(id)node
- (void)addElement:(HTMLElement *)element
{
NSUInteger existing = 0;
- for (HTMLElement *node in _list.reverseObjectEnumerator.allObjects) {
+ for (HTMLElement *node in _list.reverseObjectEnumerator) {
if ([node isEqual:[HTMLMarker marker]]) {
break;
}
diff --git a/Sources/HTMLNode.m b/Sources/HTMLNode.m
index da9d2b6..be9bb9e 100644
--- a/Sources/HTMLNode.m
+++ b/Sources/HTMLNode.m
@@ -19,6 +19,10 @@
#import "HTMLDocument+Private.h"
#import "HTMLDOMUtils.h"
+NSString * const ValidationNodePreInsertion = @"-ensurePreInsertionValidityOfNode:beforeChildNode:";
+NSString * const ValidationNodeReplacement = @"-ensureReplacementValidityOfChildNode:withNode:";
+NSString * const RemoveChildNode = @"-removeChildNode:";
+
@interface HTMLNode ()
{
NSMutableOrderedSet *_childNodes;
@@ -64,7 +68,7 @@ - (HTMLDocument *)ownerDocument
- (void)setOwnerDocument:(HTMLDocument *)ownerDocument
{
_ownerDocument = ownerDocument;
- for (HTMLNode *child in self.childNodes) {
+ for (HTMLNode *child in _childNodes) {
[child setOwnerDocument:ownerDocument];
}
}
@@ -86,12 +90,12 @@ - (HTMLElement *)parentElement
- (HTMLNode *)firstChild
{
- return self.childNodes.firstObject;
+ return _childNodes.firstObject;
}
- (HTMLNode *)lastChild
{
- return self.childNodes.lastObject;
+ return _childNodes.lastObject;
}
- (HTMLNode *)previousSibling
@@ -132,7 +136,7 @@ - (HTMLElement *)nextSiblingElement
- (NSUInteger)index
{
- return [self.parentNode indexOfChildNode:self];
+ return [_parentNode indexOfChildNode:self];
}
- (NSString *)textContent
@@ -156,12 +160,16 @@ - (HTMLElement *)asElement
- (BOOL)hasChildNodes
{
- return self.childNodes.count > 0;
+ return _childNodes.count > 0;
}
- (BOOL)hasChildNodeOfType:(HTMLNodeType)type
{
- NSUInteger index = [self.childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
+ if (_childNodes == nil) {
+ return NO;
+ }
+
+ NSUInteger index = [_childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if ([(HTMLNode *)obj nodeType] == type) {
*stop = YES;
return YES;
@@ -174,7 +182,7 @@ - (BOOL)hasChildNodeOfType:(HTMLNodeType)type
- (NSUInteger)childNodesCount
{
- return self.childNodes.count;
+ return _childNodes.count;
}
- (BOOL)isEmpty
@@ -184,25 +192,25 @@ - (BOOL)isEmpty
- (HTMLNode *)childNodeAtIndex:(NSUInteger)index
{
- return [self.childNodes objectAtIndex:index];
+ return [_childNodes objectAtIndex:index];
}
- (NSUInteger)childElementsCount
{
- return [self.childNodes indexesOfObjectsPassingTest:^BOOL(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
+ return [_childNodes indexesOfObjectsPassingTest:^BOOL(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
return node.nodeType == HTMLNodeElement;
}].count;
}
- (NSUInteger)indexOfChildNode:(HTMLNode *)node
{
- return [self.childNodes indexOfObject:node];
+ return [_childNodes indexOfObject:node];
}
- (HTMLElement *)childElementAtIndex:(NSUInteger)index
{
NSUInteger counter = 0;
- for (HTMLNode *node in self.childNodes) {
+ for (HTMLNode *node in _childNodes) {
if (node.nodeType == HTMLNodeElement) {
if (counter == index) {
return node.asElement;
@@ -216,7 +224,7 @@ - (HTMLElement *)childElementAtIndex:(NSUInteger)index
- (NSUInteger)indexOfChildElement:(HTMLElement *)element
{
NSUInteger counter = 0;
- for (HTMLNode *node in self.childNodes) {
+ for (HTMLNode *node in _childNodes) {
if (node.nodeType == HTMLNodeElement) {
if (node == element) {
return counter;
@@ -303,7 +311,7 @@ - (void)replaceAllChildNodesWithNode:(HTMLNode *)node
- (void)removeFromParentNode
{
- [self.parentNode removeChildNode:self];
+ [_parentNode removeChildNode:self];
}
- (HTMLNode *)removeChildNode:(HTMLNode *)child
@@ -311,7 +319,7 @@ - (HTMLNode *)removeChildNode:(HTMLNode *)child
if (child.parentNode != self) {
[NSException raise:HTMLKitNotFoundError
format:@"%@: Not Fount Error, removing non-child node %@. The object can not be found here.",
- NSStringFromSelector(_cmd), child];
+ RemoveChildNode, child];
}
HTMLNode *oldNode = child;
@@ -335,18 +343,18 @@ - (HTMLNode *)removeChildNodeAtIndex:(NSUInteger)index
- (void)reparentChildNodesIntoNode:(HTMLNode *)node
{
- for (HTMLNode *child in self.childNodes) {
+ for (HTMLNode *child in _childNodes) {
[node appendNode:child];
}
- [(NSMutableOrderedSet *)self.childNodes removeAllObjects];
+ [(NSMutableOrderedSet *)_childNodes removeAllObjects];
}
- (void)removeAllChildNodes
{
- for (HTMLNode *child in self.childNodes) {
+ for (HTMLNode *child in _childNodes) {
[child setParentNode:nil];
}
- [(NSMutableOrderedSet *)self.childNodes removeAllObjects];
+ [(NSMutableOrderedSet *)_childNodes removeAllObjects];
}
- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)otherNode
@@ -417,7 +425,7 @@ - (BOOL)isDescendantOfNode:(HTMLNode *)otherNode
return self.nodeType != HTMLNodeDocument && self.ownerDocument == otherNode;
}
- for (HTMLNode *parentNode = self.parentNode; parentNode; parentNode = parentNode.parentNode) {
+ for (HTMLNode *parentNode = _parentNode; parentNode; parentNode = parentNode.parentNode) {
if (parentNode == otherNode) {
return YES;
}
@@ -439,7 +447,7 @@ - (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx,
return;
}
- [self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ [_childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj, idx, stop);
}];
}
@@ -450,7 +458,7 @@ - (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInte
return;
}
- [self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ [_childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[HTMLElement class]]) {
block([obj asElement], idx, stop);
}
@@ -573,18 +581,18 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
- (void)ensurePreInsertionValidityOfNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child
{
- CheckParentValid(self, NSStringFromSelector(_cmd));
+ CheckParentValid(self, ValidationNodePreInsertion);
- CheckChildsParent(self, child, NSStringFromSelector(_cmd));
+ CheckChildsParent(self, child, ValidationNodePreInsertion);
- CheckInsertedNodeValid(node, NSStringFromSelector(_cmd));
+ CheckInsertedNodeValid(node, ValidationNodePreInsertion);
- CheckInvalidCombination(self, node, NSStringFromSelector(_cmd));
+ CheckInvalidCombination(self, node, ValidationNodePreInsertion);
void (^ hierarchyError)() = ^{
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error, inserting (%@) into (%@). The operation would yield an incorrect node tree.",
- NSStringFromSelector(_cmd), self, node];
+ ValidationNodePreInsertion, self, node];
};
if (self.nodeType == HTMLNodeDocument) {
@@ -623,18 +631,18 @@ - (void)ensurePreInsertionValidityOfNode:(HTMLNode *)node beforeChildNode:(HTMLN
- (void)ensureReplacementValidityOfChildNode:(HTMLNode *)child withNode:(HTMLNode *)node
{
- CheckParentValid(self, NSStringFromSelector(_cmd));
+ CheckParentValid(self, ValidationNodeReplacement);
- CheckChildsParent(self, child, NSStringFromSelector(_cmd));
+ CheckChildsParent(self, child, ValidationNodeReplacement);
- CheckInsertedNodeValid(node, NSStringFromSelector(_cmd));
+ CheckInsertedNodeValid(node, ValidationNodeReplacement);
- CheckInvalidCombination(self, node, NSStringFromSelector(_cmd));
+ CheckInvalidCombination(self, node, ValidationNodeReplacement);
void (^ hierarchyError)() = ^{
[NSException raise:HTMLKitHierarchyRequestError
format:@"%@: Hierarchy Request Error. The operation would yield an incorrect node tree.",
- NSStringFromSelector(_cmd)];
+ ValidationNodeReplacement];
};
void (^ checkParentHasAnotherChildOfType)(HTMLNodeType) = ^ void (HTMLNodeType type) {
@@ -690,7 +698,7 @@ - (instancetype)cloneNodeDeep:(BOOL)deep
HTMLNode *copy = [self copy];
if (deep) {
- for (HTMLNode *child in self.childNodes) {
+ for (HTMLNode *child in _childNodes) {
[copy appendNode:[child cloneNodeDeep:YES]];
}
}
diff --git a/Sources/HTMLParser.m b/Sources/HTMLParser.m
index 74844bc..2485650 100644
--- a/Sources/HTMLParser.m
+++ b/Sources/HTMLParser.m
@@ -1272,7 +1272,7 @@ - (void)processStartTagTokenInBody:(HTMLStartTagToken *)token
@"dd": @[@"dd", @"dt"],
@"dt": @[@"dd", @"dt"]};
- for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator.allObjects) {
+ for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator) {
if ([map[tagName] containsObject:node.tagName]) {
[self generateImpliedEndTagsExceptForElement:node.tagName];
if (![self.currentNode.tagName isEqualToString:node.tagName]) {
@@ -1529,7 +1529,7 @@ - (void)processEndTagTokenInBody:(HTMLEndTagToken *)token
}
[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 @@ - (void)processEndTagTokenInBody:(HTMLEndTagToken *)token
}
[_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 @@ - (void)processEndTagTokenInBody:(HTMLEndTagToken *)token
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;
}
@@ -1590,7 +1590,7 @@ - (void)processEndTagTokenInBody:(HTMLEndTagToken *)token
- (void)processAnyOtherEndTagTokenInBody:(HTMLTagToken *)token
{
- for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator.allObjects) {
+ for (HTMLElement *node in _stackOfOpenElements.reverseObjectEnumerator) {
if ([node.tagName isEqualToString:token.tagName]) {
[self generateImpliedEndTagsExceptForElement:token.tagName];
if (![node.tagName isEqualToString:self.currentNode.tagName]) {
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 @@ - (instancetype)init
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 @@ - (void)popAll
#pragma mark - Element Scope
-- (HTMLElement *)hasElementInScopeWithTagName:(NSString *)tagName;
+NS_INLINE BOOL IsSpecificScopeElement(HTMLElement *element)
{
- return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:_specificScopeElementTypes];
+ 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];
+ }
}
-- (HTMLElement *)hasAnyElementInScopeWithAnyOfTagNames:(NSArray *)tagNames
+NS_INLINE BOOL IsHeaderElement(HTMLElement *element)
{
- return [self hasAnyElementInSpecificScopeWithTagNames:tagNames andElementTypes:_specificScopeElementTypes];
+ if (element.htmlNamespace != HTMLNamespaceHTML) {
+ return NO;
+ }
+
+ return [element.tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil];
}
-- (HTMLElement *)hasElementInListItemScopeWithTagName:(NSString *)tagName
+NS_INLINE BOOL IsTableScopeElement(HTMLElement *element)
{
- NSMutableDictionary *elementTypes = [NSMutableDictionary dictionaryWithDictionary:_specificScopeElementTypes];
- [elementTypes addEntriesFromDictionary:@{@"ol": @(HTMLNamespaceHTML),
- @"ul": @(HTMLNamespaceHTML)}];
+ if (element.htmlNamespace != HTMLNamespaceHTML) {
+ return NO;
+ }
- return [self hasElementInSpecificScopeWithTagName:tagName
- andElementTypes:elementTypes];
+ return [element.tagName isEqualToAny:@"html", @"table", @"template", nil];
}
-- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName
+NS_INLINE BOOL IsListItemScopeElement(HTMLElement *element)
{
- NSMutableDictionary *elementTypes = [NSMutableDictionary dictionaryWithDictionary:_specificScopeElementTypes];
- [elementTypes addEntriesFromDictionary:@{@"button": @(HTMLNamespaceHTML)}];
+ 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;
+{
+ for (HTMLElement *node in _stack.reverseObjectEnumerator) {
+ if (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
+ return node;
+ }
+ if (IsSpecificScopeElement(node)) {
+ return nil;
+ }
+ }
+ return nil;
+}
- return [self hasElementInSpecificScopeWithTagName:tagName
- andElementTypes:elementTypes];
+- (HTMLElement *)hasHeaderElementInScope
+{
+ for (HTMLElement *node in _stack.reverseObjectEnumerator) {
+ if (IsHeaderElement(node)) {
+ return node;
+ }
+ if (IsSpecificScopeElement(node)) {
+ return nil;
+ }
+ }
+ return nil;
}
- (HTMLElement *)hasElementInTableScopeWithTagName:(NSString *)tagName
{
- return [self hasElementInSpecificScopeWithTagName:tagName
- andElementTypes:@{@"html": @(HTMLNamespaceHTML),
- @"table": @(HTMLNamespaceHTML),
- @"template": @(HTMLNamespaceHTML)}];
+ 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
{
- return [self hasAnyElementInSpecificScopeWithTagNames:tagNames
- andElementTypes:@{@"html": @(HTMLNamespaceHTML),
- @"table": @(HTMLNamespaceHTML),
- @"template": @(HTMLNamespaceHTML)}];
+ for (HTMLElement *node in _stack.reverseObjectEnumerator) {
+ if (node.htmlNamespace == HTMLNamespaceHTML && [tagNames containsObject:node.tagName]) {
+ return node;
+ }
+ if (IsTableScopeElement(node)) {
+ return nil;
+ }
+ }
+ return nil;
}
-- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
+- (HTMLElement *)hasElementInListItemScopeWithTagName:(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) || IsListItemScopeElement(node)) {
return nil;
}
}
return nil;
}
-- (HTMLElement *)hasElementInSpecificScopeWithTagName:(NSString *)tagName
- andElementTypes:(NSDictionary *)elementTypes
+- (HTMLElement *)hasElementInButtonScopeWithTagName:(NSString *)tagName
{
- return [self hasAnyElementInSpecificScopeWithTagNames:@[tagName] andElementTypes:elementTypes];
+ 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 *)hasAnyElementInSpecificScopeWithTagNames:(NSArray *)tagNames
- andElementTypes:(NSDictionary *)elementTypes
+- (HTMLElement *)hasElementInSelectScopeWithTagName:(NSString *)tagName
{
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 (node.htmlNamespace == HTMLNamespaceHTML && [tagName isEqualToString:node.tagName]) {
+ return node;
}
- if ([elementTypes[node.tagName] isEqual:@(node.htmlNamespace)]) {
+ 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;
/**