From d08ac2100e439180a555b5f56d529e79bbc70936 Mon Sep 17 00:00:00 2001 From: iska Date: Tue, 21 Apr 2015 23:07:57 +0200 Subject: [PATCH 01/55] Fix the isEqualTo call in Parser Use isEqual instead --- HTMLKit/HTMLListOfActiveFormattingElements.m | 2 +- HTMLKit/HTMLParser.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HTMLKit/HTMLListOfActiveFormattingElements.m b/HTMLKit/HTMLListOfActiveFormattingElements.m index 97092c7..d0fb8b0 100644 --- a/HTMLKit/HTMLListOfActiveFormattingElements.m +++ b/HTMLKit/HTMLListOfActiveFormattingElements.m @@ -52,7 +52,7 @@ } if (node.htmlNamespace == element.htmlNamespace && [node.tagName isEqualToString:element.tagName] && - [node.attributes isEqualTo:element.attributes]) { + [node.attributes isEqual:element.attributes]) { existing++; } if (existing == 3) { diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m index 5a6e759..f5f0ed5 100644 --- a/HTMLKit/HTMLParser.m +++ b/HTMLKit/HTMLParser.m @@ -127,7 +127,7 @@ return nil; } - if ([_contextElement isEqualTo:contextElement]) { + if ([_contextElement isEqual:contextElement]) { return [_document childNodeAtIndex:0].childNodes.array; } From 429f00adb6fd26e736c5e4ee577d02f9d8301037 Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 23 Apr 2015 00:40:35 +0200 Subject: [PATCH 02/55] Fix project settings after update to Xcode 6.3.1 --- HTMLKit.xcodeproj/project.pbxproj | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 1a94f56..275fa04 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -116,6 +116,14 @@ 62AE594B19F9948A0043F069 /* HTMLCharacterToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */; }; 62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; }; 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; }; + 62D89DB01AE7080300B6243D /* HTMLNodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */; }; + 62D89DB11AE7081600B6243D /* HTMLTokenizerStates.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */; }; + 62D89DB21AE7081600B6243D /* HTMLTokenizerCharacters.h in Headers */ = {isa = PBXBuildFile; fileRef = 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */; }; + 62D89DB31AE7081600B6243D /* HTMLNamespaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */; }; + 62D89DB41AE7081600B6243D /* HTMLElementTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */; }; + 62D89DB51AE7081600B6243D /* HTMLElementAdjustment.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */; }; + 62D89DB61AE7081600B6243D /* HTMLParserInsertionModes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */; }; + 62D89DB71AE7081600B6243D /* HTMLQuirksMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */; }; 62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */; }; 62F31FDE19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */; }; 62F31FDF19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */; }; @@ -523,6 +531,14 @@ buildActionMask = 2147483647; files = ( 6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */, + 62D89DB11AE7081600B6243D /* HTMLTokenizerStates.h in Headers */, + 62D89DB21AE7081600B6243D /* HTMLTokenizerCharacters.h in Headers */, + 62D89DB31AE7081600B6243D /* HTMLNamespaces.h in Headers */, + 62D89DB41AE7081600B6243D /* HTMLElementTypes.h in Headers */, + 62D89DB51AE7081600B6243D /* HTMLElementAdjustment.h in Headers */, + 62D89DB61AE7081600B6243D /* HTMLParserInsertionModes.h in Headers */, + 62D89DB71AE7081600B6243D /* HTMLQuirksMode.h in Headers */, + 62D89DB01AE7080300B6243D /* HTMLNodes.h in Headers */, 62F873EB19E088C90062683C /* HTMLParser.h in Headers */, 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */, 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */, @@ -938,6 +954,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", + "$(SDKROOT)/Developer/Library/Frameworks", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; @@ -960,6 +977,7 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", + "$(SDKROOT)/Developer/Library/Frameworks", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; From 1bc896efc9f465a80f14b5f33b7fd0adcbe5225e Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 23 Apr 2015 00:43:06 +0200 Subject: [PATCH 03/55] Add tests for HTML Node methods Todo: Tests for negative cases, e.g. invalid manipulations that lead to hierarchy or not found errors. --- HTMLKit.xcodeproj/project.pbxproj | 6 + HTMLKit/HTMLNode.h | 4 + HTMLKit/HTMLNode.m | 10 +- HTMLKitTests/HTMLKitNodesTests.m | 406 ++++++++++++++++++++++++++++++ 4 files changed, 424 insertions(+), 2 deletions(-) create mode 100644 HTMLKitTests/HTMLKitNodesTests.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 275fa04..5422c48 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -80,6 +80,8 @@ 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; }; 624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; }; 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; }; + 624FC37A1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */; }; + 624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */; }; 625A14B019C7829400AD0C32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AF19C7829400AD0C32 /* Cocoa.framework */; }; 625A14BA19C7829400AD0C32 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 625A14B819C7829400AD0C32 /* InfoPlist.strings */; }; 625A14BE19C7829400AD0C32 /* HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A14BD19C7829400AD0C32 /* HTMLKit.m */; }; @@ -214,6 +216,7 @@ 624AC90D19FC702E00BD3C4A /* HTML Standard.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "HTML Standard.html"; sourceTree = ""; }; 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitStringCategoryTests.m; sourceTree = ""; }; 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitOrderedDictionaryTests.m; sourceTree = ""; }; + 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodesTests.m; sourceTree = ""; }; 625A14AC19C7829400AD0C32 /* HTMLKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HTMLKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 625A14AF19C7829400AD0C32 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 625A14B219C7829400AD0C32 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -350,6 +353,7 @@ children = ( 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */, 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, + 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */, ); name = Nodes; sourceTree = ""; @@ -758,6 +762,7 @@ 623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, 6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, + 624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -808,6 +813,7 @@ 623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, 62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, 6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, + 624FC37A1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index 00d1f08..b07c56f 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -53,6 +53,8 @@ typedef NS_ENUM(short, HTMLNodeType) @property (nonatomic, copy) NSString *textContent; +- (instancetype)init NS_UNAVAILABLE; + - (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type; - (BOOL)hasChildNodes; @@ -75,6 +77,8 @@ typedef NS_ENUM(short, HTMLNodeType) - (void)replaceAllChildNodesWithNode:(HTMLNode *)node; +- (void)removeFromParentNode; + - (HTMLNode *)removeChildNode:(HTMLNode *)node; - (HTMLNode *)removeChildNodeAtIndex:(NSUInteger)index; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 368a196..13dd6b8 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -168,6 +168,7 @@ NSUInteger index = [self indexOfChildNode:child]; node.parentNode = self; [(NSMutableOrderedSet *)self.childNodes replaceObjectAtIndex:index withObject:node]; + [child removeFromParentNode]; return child; } @@ -181,6 +182,11 @@ } } +- (void)removeFromParentNode +{ + [self.parentNode removeChildNode:self]; +} + - (HTMLNode *)removeChildNode:(HTMLNode *)child { if (child.parentNode != self) { @@ -205,12 +211,12 @@ for (HTMLNode *child in self.childNodes.array) { [node appendNode:child]; } - [self removeAllChildNodes]; + [(NSMutableOrderedSet *)self.childNodes removeAllObjects]; } - (void)removeAllChildNodes { - [(NSMutableOrderedSet *)self.childNodes removeAllObjects]; + [self.childNodes.array makeObjectsPerformSelector:@selector(removeFromParentNode)]; } #pragma mark - Enumeration diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m new file mode 100644 index 0000000..56a5d6a --- /dev/null +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -0,0 +1,406 @@ +// +// HTMLKitNodesTests.m +// HTMLKit +// +// Created by Iska on 20/04/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLNodes.h" + +@interface HTMLKitNodesTests : XCTestCase + +@end + +@implementation HTMLKitNodesTests + +- (void)setUp +{ + [super setUp]; +} + +- (void)tearDown +{ + [super tearDown]; +} + +- (void)testInitNode +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + XCTAssertNotNil(node); + XCTAssertEqualObjects(node.name, @"name"); + XCTAssertEqual(node.type, HTMLNodeElement); + XCTAssertNotNil(node.childNodes); + XCTAssertEqual(node.childNodes.count, 0); + + XCTAssertNil(node.ownerDocument); + XCTAssertNil(node.baseURI); + XCTAssertNil(node.parentNode); + XCTAssertNil(node.parentElement); + XCTAssertNil(node.firstChiledNode); + XCTAssertNil(node.lastChildNode); + XCTAssertNil(node.previousSibling); + XCTAssertNil(node.lastChildNode); +} + +- (void)testParentNode +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + XCTAssertNil(firstChild.parentNode); + + [node appendNode:firstChild]; + XCTAssertTrue(node.hasChildNodes); + XCTAssertEqual(node.childNodesCount, 1); + + XCTAssertNotNil(firstChild.parentNode); + XCTAssertEqualObjects(firstChild.parentNode, node); + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + + [node appendNodes:@[secondChild, thirdChild]]; + XCTAssertTrue(node.hasChildNodes); + XCTAssertEqual(node.childNodesCount, 3); + + XCTAssertNotNil(secondChild.parentNode); + XCTAssertEqualObjects(secondChild.parentNode, node); + + XCTAssertNotNil(thirdChild.parentNode); + XCTAssertEqualObjects(thirdChild.parentNode, node); +} + +- (void)testChildNodesCount +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + XCTAssertFalse(node.hasChildNodes); + XCTAssertEqual(node.childNodesCount, 0); + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + XCTAssertTrue(node.hasChildNodes); + XCTAssertEqual(node.childNodesCount, 1); + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + XCTAssertTrue(node.hasChildNodes); + XCTAssertEqual(node.childNodesCount, 2); + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + XCTAssertTrue(node.hasChildNodes); + XCTAssertEqual(node.childNodesCount, 3); +} + +- (void)testFirstAndLastChildNodes +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + XCTAssertEqualObjects(node.firstChiledNode, firstChild); + XCTAssertEqualObjects(node.lastChildNode, firstChild); + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + XCTAssertEqualObjects(node.firstChiledNode, firstChild); + XCTAssertEqualObjects(node.lastChildNode, secondChild); + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + XCTAssertEqualObjects(node.firstChiledNode, firstChild); + XCTAssertEqualObjects(node.lastChildNode, thirdChild); +} + +- (void)testNextAndPreviousSiblingNodes +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + XCTAssertNil(firstChild.previousSibling); + XCTAssertEqualObjects(firstChild.nextSibling, secondChild); + + XCTAssertEqualObjects(secondChild.previousSibling, firstChild); + XCTAssertEqualObjects(secondChild.nextSibling, thirdChild); + + XCTAssertEqualObjects(thirdChild.previousSibling, secondChild); + XCTAssertNil(thirdChild.nextSibling); +} + +- (void)testHasChildNodeOfType +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + XCTAssertFalse([node hasChildNodeOfType:HTMLNodeElement]); + XCTAssertFalse([node hasChildNodeOfType:HTMLNodeText]); + XCTAssertFalse([node hasChildNodeOfType:HTMLNodeComment]); + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + XCTAssertTrue([node hasChildNodeOfType:HTMLNodeElement]); + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeText]; + [node appendNode:secondChild]; + + XCTAssertTrue([node hasChildNodeOfType:HTMLNodeText]); + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeComment]; + [node appendNode:thirdChild]; + + XCTAssertTrue([node hasChildNodeOfType:HTMLNodeComment]); +} + +- (void)testChildNodeAtIndex +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + XCTAssertEqualObjects([node childNodeAtIndex:0], firstChild); + XCTAssertEqualObjects([node childNodeAtIndex:1], secondChild); + XCTAssertEqualObjects([node childNodeAtIndex:2], thirdChild); +} + +- (void)testIndexOfChildNode +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + XCTAssertEqual([node indexOfChildNode:firstChild], 0); + XCTAssertEqual([node indexOfChildNode:secondChild], 1); + XCTAssertEqual([node indexOfChildNode:thirdChild], 2); +} + +- (void)testIndertNodeBeforeChild +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node insertNode:secondChild beforeChildNode:firstChild]; + + XCTAssertEqual([node indexOfChildNode:firstChild], 1); + XCTAssertEqual([node indexOfChildNode:secondChild], 0); + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node insertNode:thirdChild beforeChildNode:firstChild]; + + XCTAssertEqual([node indexOfChildNode:firstChild], 2); + XCTAssertEqual([node indexOfChildNode:secondChild], 0); + XCTAssertEqual([node indexOfChildNode:thirdChild], 1); +} + +- (void)testReplaceChildNode +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node replaceChildNode:firstChild withNode:thirdChild]; + + XCTAssertNil(firstChild.parentNode); + + XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:secondChild], 1); + XCTAssertEqual([node indexOfChildNode:thirdChild], 0); +} + +- (void)testReplaceAllChildNodes +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node replaceAllChildNodesWithNode:thirdChild]; + + XCTAssertNil(firstChild.parentNode); + XCTAssertNil(secondChild.parentNode); + XCTAssertEqualObjects(thirdChild.parentNode, node); + + XCTAssertEqual(node.childNodesCount, 1); + XCTAssertEqual([node indexOfChildNode:thirdChild], 0); +} + +- (void)testRemoveFromParentNode +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + [firstChild removeFromParentNode]; + + XCTAssertNil(firstChild.parentNode); + XCTAssertEqual(node.childNodesCount, 0); + XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound); +} + +- (void)testRemoveChildNode +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + [node removeChildNode:secondChild]; + XCTAssertEqual(node.childNodesCount, 2); + + XCTAssertEqualObjects(firstChild.parentNode, node); + XCTAssertNil(secondChild.parentNode); + XCTAssertEqualObjects(thirdChild.parentNode, node); + + XCTAssertEqual([node indexOfChildNode:firstChild], 0); + XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:thirdChild], 1); + + [node removeChildNode:firstChild]; + XCTAssertEqual(node.childNodesCount, 1); + + XCTAssertNil(firstChild.parentNode); + XCTAssertNil(secondChild.parentNode); + XCTAssertEqualObjects(thirdChild.parentNode, node); + + XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:thirdChild], 0); +} + +- (void)testRemoveChildNodeAtIndex +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + [node removeChildNodeAtIndex:1]; + XCTAssertEqual(node.childNodesCount, 2); + + XCTAssertEqualObjects(firstChild.parentNode, node); + XCTAssertNil(secondChild.parentNode); + XCTAssertEqualObjects(thirdChild.parentNode, node); + + XCTAssertEqual([node indexOfChildNode:firstChild], 0); + XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:thirdChild], 1); + + [node removeChildNodeAtIndex:0]; + XCTAssertEqual(node.childNodesCount, 1); + + XCTAssertNil(firstChild.parentNode); + XCTAssertNil(secondChild.parentNode); + XCTAssertEqualObjects(thirdChild.parentNode, node); + + XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:thirdChild], 0); +} + +- (void)testRemoveAllChildNodes +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + [node removeAllChildNodes]; + XCTAssertEqual(node.childNodesCount, 0); + + XCTAssertNil(firstChild.parentNode); + XCTAssertNil(secondChild.parentNode); + XCTAssertNil(thirdChild.parentNode); + + XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:thirdChild], NSNotFound); +} + +- (void)testReparentChildNodes +{ + HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; + + HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; + [node appendNode:firstChild]; + + HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; + [node appendNode:secondChild]; + + HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; + [node appendNode:thirdChild]; + + HTMLNode *newParent = [[HTMLNode alloc] initWithName:@"new-parent" type:HTMLNodeElement]; + + [node reparentChildNodesIntoNode:newParent]; + XCTAssertEqual(node.childNodesCount, 0); + + XCTAssertEqual([node indexOfChildNode:firstChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:secondChild], NSNotFound); + XCTAssertEqual([node indexOfChildNode:thirdChild], NSNotFound); + + XCTAssertEqualObjects(firstChild.parentNode, newParent); + XCTAssertEqualObjects(secondChild.parentNode, newParent); + XCTAssertEqualObjects(thirdChild.parentNode, newParent); +} + +@end From 071742fb27a3a8dbd55f8e5d1ca2679b49b93fd8 Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 25 Apr 2015 13:17:02 +0200 Subject: [PATCH 04/55] Add default initializers for HTML Comment, Document Type & Document Fragment --- HTMLKit/HTMLComment.m | 5 +++++ HTMLKit/HTMLDocumentFragment.m | 5 +++++ HTMLKit/HTMLDocumentType.m | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/HTMLKit/HTMLComment.m b/HTMLKit/HTMLComment.m index 23194bc..dbe2ac4 100644 --- a/HTMLKit/HTMLComment.m +++ b/HTMLKit/HTMLComment.m @@ -10,6 +10,11 @@ @implementation HTMLComment +- (instancetype)init +{ + return [self initWithData:nil]; +} + - (instancetype)initWithData:(NSString *)data { self = [super initWithName:@"#comment" type:HTMLNodeComment]; diff --git a/HTMLKit/HTMLDocumentFragment.m b/HTMLKit/HTMLDocumentFragment.m index fc03c9a..6b549a8 100644 --- a/HTMLKit/HTMLDocumentFragment.m +++ b/HTMLKit/HTMLDocumentFragment.m @@ -15,6 +15,11 @@ @implementation HTMLDocumentFragment +- (instancetype)init +{ + return [self initWithDocument:nil]; +} + - (instancetype)initWithDocument:(HTMLDocument *)document { self = [super initWithName:@"#document-fragment" type:HTMLNodeDocumentFragment]; diff --git a/HTMLKit/HTMLDocumentType.m b/HTMLKit/HTMLDocumentType.m index ce6687f..a7e8165 100644 --- a/HTMLKit/HTMLDocumentType.m +++ b/HTMLKit/HTMLDocumentType.m @@ -22,6 +22,11 @@ NS_INLINE BOOL nilOrEqual(id first, id second) { @implementation HTMLDocumentType +- (instancetype)init +{ + return [self initWithName:@"html" publicIdentifier:nil systemIdentifier:nil]; +} + - (instancetype)initWithName:(NSString *)name publicIdentifier:(NSString *)publicIdentifier systemIdentifier:(NSString *)systemIdentifier From 3ddfa15b150417e819705c7768ce9f3338d2703b Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 25 Apr 2015 13:20:15 +0200 Subject: [PATCH 05/55] Add tests to validate the insertions of nodes into a given parent These tests check the implementation of the Mutation Algorithms of the DOM Standard https://dom.spec.whatwg.org/#mutation-algorithms --- HTMLKitTests/HTMLKitNodesTests.m | 134 +++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index 56a5d6a..a38959d 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -403,4 +403,138 @@ XCTAssertEqualObjects(thirdChild.parentNode, newParent); } +- (void)testValidParentNodeWhenAppending +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + + id parent = [HTMLDocument new]; + XCTAssertNoThrow([parent appendNode:element]); + + parent = [HTMLDocumentFragment new]; + XCTAssertNoThrow([parent appendNode:element]); + + parent = [HTMLElement new]; + XCTAssertNoThrow([parent appendNode:element]); + + parent = [HTMLTemplate new]; + XCTAssertNoThrow([parent appendNode:element]); + + parent = [HTMLDocumentType new]; + XCTAssertThrows([parent appendNode:element]); + + parent = [HTMLComment new]; + XCTAssertThrows([parent appendNode:element]); + + parent = [HTMLText new]; + XCTAssertThrows([parent appendNode:element]); +} + +- (void)testValidParentNodeWhenInserting +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + + id parent = [HTMLDocument new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLDocumentFragment new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLElement new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLTemplate new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLDocumentType new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLComment new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLText new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); +} + +- (void)testValidChildNodeWhenInserting +{ + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; + [div appendNode:p]; + + HTMLElement *table = [[HTMLElement alloc] initWithTagName:@"table"]; + + XCTAssertNoThrow([div insertNode:table beforeChildNode:p]); + + [div removeChildNode:p]; + + HTMLElement *section = [[HTMLElement alloc] initWithTagName:@"section"]; + XCTAssertThrows([div insertNode:section beforeChildNode:p]); +} + +- (void)testValidChildNodeWhenReplacing +{ + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; + [div appendNode:p]; + + HTMLElement *table = [[HTMLElement alloc] initWithTagName:@"table"]; + + XCTAssertNoThrow([div replaceChildNode:p withNode:table]); + + XCTAssertThrows([div replaceChildNode:p withNode:[HTMLComment new]]); +} + +- (void)testValidChildNodeWhenRemoving +{ + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; + [div appendNode:p]; + + XCTAssertNoThrow([div removeChildNode:p]); + XCTAssertThrows([div removeChildNode:p]); +} + +- (void)testValidInsertedNode +{ + HTMLDocument *document = [HTMLDocument new]; + + XCTAssertNoThrow([document appendNode:[HTMLDocumentFragment new]]); + XCTAssertNoThrow([document appendNode:[HTMLDocumentType new]]); + XCTAssertNoThrow([document appendNode:[HTMLElement new]]); + + HTMLElement *element = [HTMLElement new]; + + XCTAssertNoThrow([element appendNode:[HTMLTemplate new]]); + XCTAssertNoThrow([element appendNode:[HTMLComment new]]); + XCTAssertNoThrow([element appendNode:[HTMLText new]]); +} + +- (void)testValidParentForDoctype +{ + HTMLDocumentType *doctype = [HTMLDocumentType new]; + + XCTAssertNoThrow([[HTMLDocument new] appendNode:doctype]); + + XCTAssertThrows([[HTMLDocumentFragment new] appendNode:doctype]); + XCTAssertThrows([[HTMLDocumentType new] appendNode:doctype]); + XCTAssertThrows([[HTMLElement new] appendNode:doctype]); + XCTAssertThrows([[HTMLTemplate new] appendNode:doctype]); + XCTAssertThrows([[HTMLComment new] appendNode:doctype]); + XCTAssertThrows([[HTMLText new] appendNode:doctype]); +} + +- (void)testValidParentForText +{ + HTMLText *text = [HTMLText new]; + + XCTAssertThrows([[HTMLDocument new] appendNode:text]); + XCTAssertThrows([[HTMLDocumentType new] appendNode:text]); + XCTAssertThrows([[HTMLComment new] appendNode:text]); + XCTAssertThrows([[HTMLText new] appendNode:text]); + + XCTAssertNoThrow([[HTMLDocumentFragment new] appendNode:text]); + XCTAssertNoThrow([[HTMLElement new] appendNode:text]); + XCTAssertNoThrow([[HTMLTemplate new] appendNode:text]); +} + @end From 36960dceb368474b0c83993f988604b3e91d602e Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 25 Apr 2015 13:21:08 +0200 Subject: [PATCH 06/55] Rename node parameter in the validation method of HTML Node for clarity --- HTMLKit/HTMLNode.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 13dd6b8..be7a196 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -258,14 +258,14 @@ return node; } -NS_INLINE void CheckParentValid(HTMLNode *node, NSString *cmd) +NS_INLINE void CheckParentValid(HTMLNode *parent, NSString *cmd) { - if (node.type != HTMLNodeDocument && - node.type != HTMLNodeDocumentFragment && - node.type != HTMLNodeElement) { + if (parent.type != HTMLNodeDocument && + parent.type != HTMLNodeDocumentFragment && + parent.type != HTMLNodeElement) { [NSException raise:HTMLKitHierarchyRequestError format:@"%@: Hierarchy Request Error, inserting into %@ is not allowed. The operation would yield an incorrect node tree.", - cmd, node.name]; + cmd, parent.name]; } } From 71aed89574b69d823ea04c0ecb7f3ec6377417a1 Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 25 Apr 2015 13:21:40 +0200 Subject: [PATCH 07/55] Fix validation for inserting a Document Fragment into a Document --- HTMLKit/HTMLNode.m | 12 +++++------ HTMLKitTests/HTMLKitNodesTests.m | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index be7a196..f2751e9 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -319,18 +319,18 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin void (^ hierarchyError)() = ^{ [NSException raise:HTMLKitHierarchyRequestError - format:@"%@: Hierarchy Request Error. The operation would yield an incorrect node tree.", - NSStringFromSelector(_cmd)]; + format:@"%@: Hierarchy Request Error, inserting (%@) into (%@). The operation would yield an incorrect node tree.", + NSStringFromSelector(_cmd), self, node]; }; if (self.type == HTMLNodeDocument) { switch (node.type) { case HTMLNodeDocumentFragment: - if (self.childNodesCount > 1 || - [self hasChildNodeOfType:HTMLNodeText]) { + if (node.childNodesCount > 1 || + [node hasChildNodeOfType:HTMLNodeText]) { hierarchyError(); - } else if (self.childNodesCount == 1) { - if (self.hasChildNodes || + } else if (node.childNodesCount == 1) { + if ([self hasChildNodeOfType:HTMLNodeElement] || child.type == HTMLNodeDocumentType || child.nextSibling.type == HTMLNodeDocumentType) { hierarchyError(); diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index a38959d..9c2dccc 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -537,4 +537,41 @@ XCTAssertNoThrow([[HTMLTemplate new] appendNode:text]); } +- (void)testValidDocumentFragmentInsertionIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:document]; + + void (^ reset)() = ^ { + [fragment removeAllChildNodes]; + [document removeAllChildNodes]; + }; + + [fragment appendNode:[HTMLText new]]; + XCTAssertThrows([document appendNode:fragment]); + + reset(); + [fragment appendNode:[HTMLElement new]]; + [fragment appendNode:[HTMLElement new]]; + XCTAssertThrows([document appendNode:fragment]); + + reset(); + [fragment appendNode:[HTMLElement new]]; + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document appendNode:fragment]); + + reset(); + HTMLDocumentType *doctype = [HTMLDocumentType new]; + [fragment appendNode:[HTMLElement new]]; + [document appendNode:doctype]; + XCTAssertThrows([document insertNode:fragment beforeChildNode:doctype]); + + reset(); + HTMLComment *doctypePreviousSibling = [HTMLComment new]; + [fragment appendNode:[HTMLElement new]]; + [document appendNode:doctypePreviousSibling]; + [document appendNode:doctype]; + XCTAssertThrows([document insertNode:fragment beforeChildNode:doctypePreviousSibling]); +} + @end From bc6bbf1d637f1dd6b5a6c4308f0a9f6157d1aa1b Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 25 Apr 2015 15:27:25 +0200 Subject: [PATCH 08/55] Add tests for validation for inserting an Element & Document Type into a Document --- HTMLKitTests/HTMLKitNodesTests.m | 54 ++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index 9c2dccc..73b0522 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -446,13 +446,13 @@ XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); parent = [HTMLDocumentType new]; - XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); parent = [HTMLComment new]; - XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); parent = [HTMLText new]; - XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); } - (void)testValidChildNodeWhenInserting @@ -574,4 +574,52 @@ XCTAssertThrows([document insertNode:fragment beforeChildNode:doctypePreviousSibling]); } +- (void)testValidElementInsertionIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLElement *element = [HTMLElement new]; + + void (^ reset)() = ^ { + [element removeAllChildNodes]; + [document removeAllChildNodes]; + }; + + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document appendNode:element]); + + reset(); + HTMLDocumentType *doctype = [HTMLDocumentType new]; + [document appendNode:doctype]; + XCTAssertThrows([document insertNode:element beforeChildNode:doctype]); + + reset(); + HTMLComment *doctypePreviousSibling = [HTMLComment new]; + [document appendNode:doctypePreviousSibling]; + [document appendNode:doctype]; + XCTAssertThrows([document insertNode:element beforeChildNode:doctypePreviousSibling]); +} + +- (void)testValidDoctypeInsertionIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLDocumentType *doctype = [HTMLDocumentType new]; + + void (^ reset)() = ^ { + [document removeAllChildNodes]; + }; + + [document appendNode:[HTMLDocumentType new]]; + XCTAssertThrows([document appendNode:doctype]); + + reset(); + HTMLComment *secondChild = [HTMLComment new]; + [document appendNode:[HTMLElement new]]; + [document appendNode:secondChild]; + XCTAssertThrows([document insertNode:doctype beforeChildNode:secondChild]); + + reset(); + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document appendNode:doctype]); +} + @end From cffe1cc7030f437ede1f48f68e64de2600b4e6c6 Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 25 Apr 2015 22:25:31 +0200 Subject: [PATCH 09/55] Fix validation for replacing a child node and refactor common logic into a block --- HTMLKit/HTMLNode.m | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index f2751e9..6b211be 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -373,17 +373,26 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin NSStringFromSelector(_cmd)]; }; + void (^ checkParentHasAnotherChildOfType)(HTMLNodeType) = ^ void (HTMLNodeType type) { + [self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) { + if (node.type == type && node != child) { + *stop = YES; + hierarchyError(); + } + }]; + }; + if (self.type == HTMLNodeDocument) { switch (node.type) { case HTMLNodeDocumentFragment: - if (self.childNodesCount > 1 || - [self hasChildNodeOfType:HTMLNodeText]) { + if (node.childNodesCount > 1 || + [node hasChildNodeOfType:HTMLNodeText]) { hierarchyError(); - } else if (self.childNodesCount == 1) { - if (self.firstChiledNode != node || - child.nextSibling.type == HTMLNodeDocumentType) { + } else if (node.childNodesCount == 1) { + if (child.nextSibling.type == HTMLNodeDocumentType) { hierarchyError(); } + checkParentHasAnotherChildOfType(HTMLNodeElement); } break; case HTMLNodeElement: @@ -391,12 +400,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin if (child.nextSibling.type == HTMLNodeDocumentType) { hierarchyError(); } - [self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) { - if (node.type == HTMLNodeElement && node != child) { - *stop = YES; - hierarchyError(); - } - }]; + checkParentHasAnotherChildOfType(HTMLNodeElement); break; } case HTMLNodeDocumentType: @@ -404,12 +408,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin if (child.previousSibling.type == HTMLNodeElement) { hierarchyError(); } - [self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) { - if (node.type == HTMLNodeDocument && node != child) { - *stop = YES; - hierarchyError(); - } - }]; + checkParentHasAnotherChildOfType(HTMLNodeDocumentType); break; } default: From b2f1864c80b992cda67084a970f2e2814e61867b Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 25 Apr 2015 22:31:12 +0200 Subject: [PATCH 10/55] Add tests for validating the replacing child node within a Document --- HTMLKitTests/HTMLKitNodesTests.m | 99 ++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index 73b0522..ccd3f10 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -622,4 +622,103 @@ XCTAssertThrows([document appendNode:doctype]); } +- (void)testValidDocumentFragmentReplacementIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLComment *child = [HTMLComment new]; + HTMLDocumentFragment *replacement = [[HTMLDocumentFragment alloc] initWithDocument:document]; + + void (^ reset)() = ^ { + [replacement removeAllChildNodes]; + [document removeAllChildNodes]; + [document appendNode:child]; + }; + + /** + * Replacement Fragment has a Text node child + */ + [replacement appendNode:[HTMLText new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * Replacement Fragment has more than one Element child + */ + reset(); + [replacement appendNode:[HTMLElement new]]; + [replacement appendNode:[HTMLElement new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * Replacement Fragment has one Element child + * Document has an Element child that is not the Replacement + */ + reset(); + [replacement appendNode:[HTMLElement new]]; + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * Replacement Fragment has one Element child + * Doctype is following the child node + */ + reset(); + HTMLDocumentType *doctype = [HTMLDocumentType new]; + [replacement appendNode:[HTMLElement new]]; + [document appendNode:doctype]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); +} + +- (void)testValidElementReplacementIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLComment *child = [HTMLComment new]; + HTMLElement *replacement = [HTMLElement new]; + + void (^ reset)() = ^ { + [replacement removeAllChildNodes]; + [document removeAllChildNodes]; + [document appendNode:child]; + }; + + /** + * Docment has an Element child that is not replacement + */ + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * Doctype is following the child node + */ + reset(); + HTMLDocumentType *doctype = [HTMLDocumentType new]; + [document appendNode:doctype]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); +} + +- (void)testValidDoctypeReplacementIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLComment *child = [HTMLComment new]; + HTMLDocumentType *replacement = [HTMLDocumentType new]; + + void (^ reset)() = ^ { + [replacement removeAllChildNodes]; + [document removeAllChildNodes]; + [document appendNode:child]; + }; + + /** + * Docment has an Doctype child that is not replacement + */ + [document appendNode:[HTMLDocumentType new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * An Element is preceding the child node + */ + reset(); + [document insertNode:[HTMLElement new] beforeChildNode:child]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); +} + @end From a2c6f22495c0af82c34287a59744722045564f01 Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 25 Apr 2015 22:31:47 +0200 Subject: [PATCH 11/55] Add comments for insertion and replacement tests --- HTMLKitTests/HTMLKitNodesTests.m | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index ccd3f10..0625c11 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -547,25 +547,44 @@ [document removeAllChildNodes]; }; + /** + * Fragment has a Text node child + */ [fragment appendNode:[HTMLText new]]; XCTAssertThrows([document appendNode:fragment]); + /** + * Fragment has more than one Element child + */ reset(); [fragment appendNode:[HTMLElement new]]; [fragment appendNode:[HTMLElement new]]; XCTAssertThrows([document appendNode:fragment]); + + /** + * Fragment has one node child + * Document has an Element child + */ reset(); [fragment appendNode:[HTMLElement new]]; [document appendNode:[HTMLElement new]]; XCTAssertThrows([document appendNode:fragment]); + /** + * Fragment has one node child + * "before child" is a Doctype + */ reset(); HTMLDocumentType *doctype = [HTMLDocumentType new]; [fragment appendNode:[HTMLElement new]]; [document appendNode:doctype]; XCTAssertThrows([document insertNode:fragment beforeChildNode:doctype]); + /** + * Fragment has one node child + * "before child" is following a Doctype + */ reset(); HTMLComment *doctypePreviousSibling = [HTMLComment new]; [fragment appendNode:[HTMLElement new]]; @@ -584,14 +603,23 @@ [document removeAllChildNodes]; }; + /** + * Document has an Element child + */ [document appendNode:[HTMLElement new]]; XCTAssertThrows([document appendNode:element]); + /** + * "before child" is a Doctype + */ reset(); HTMLDocumentType *doctype = [HTMLDocumentType new]; [document appendNode:doctype]; XCTAssertThrows([document insertNode:element beforeChildNode:doctype]); + /** + * Doctype is following the "before child" + */ reset(); HTMLComment *doctypePreviousSibling = [HTMLComment new]; [document appendNode:doctypePreviousSibling]; @@ -608,15 +636,24 @@ [document removeAllChildNodes]; }; + /** + * Document has a Doctype child + */ [document appendNode:[HTMLDocumentType new]]; XCTAssertThrows([document appendNode:doctype]); + /** + * An Element is preceding the "before child" + */ reset(); HTMLComment *secondChild = [HTMLComment new]; [document appendNode:[HTMLElement new]]; [document appendNode:secondChild]; XCTAssertThrows([document insertNode:doctype beforeChildNode:secondChild]); + /** + * Document has an Element child + */ reset(); [document appendNode:[HTMLElement new]]; XCTAssertThrows([document appendNode:doctype]); From 40baea8ece14a16816fd6f148a16dac2410abf5d Mon Sep 17 00:00:00 2001 From: iska Date: Mon, 27 Apr 2015 19:43:43 +0200 Subject: [PATCH 12/55] Move tests for Mutation Algorithms into separate class --- HTMLKit.xcodeproj/project.pbxproj | 6 + HTMLKitTests/HTMLKitMutationAlgorithmsTests.m | 373 ++++++++++++++++++ HTMLKitTests/HTMLKitNodesTests.m | 355 ----------------- 3 files changed, 379 insertions(+), 355 deletions(-) create mode 100644 HTMLKitTests/HTMLKitMutationAlgorithmsTests.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 5422c48..e00d8ff 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -126,6 +126,8 @@ 62D89DB51AE7081600B6243D /* HTMLElementAdjustment.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */; }; 62D89DB61AE7081600B6243D /* HTMLParserInsertionModes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */; }; 62D89DB71AE7081600B6243D /* HTMLQuirksMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */; }; + 62EC7AE61AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */; }; + 62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */; }; 62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */; }; 62F31FDE19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */; }; 62F31FDF19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */; }; @@ -253,6 +255,7 @@ 62D8345719FB1AC4009205A9 /* HTML5LibTokenizerTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTML5LibTokenizerTest.h; sourceTree = ""; }; 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTML5LibTokenizerTest.m; sourceTree = ""; }; 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerCharacters.h; sourceTree = ""; }; + 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitMutationAlgorithmsTests.m; sourceTree = ""; }; 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerEntities.h; sourceTree = ""; }; 62F31FDC19E9DCCF007F0657 /* HTMLTokenizerEntities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTokenizerEntities.m; sourceTree = ""; }; 62F873E919E088C90062683C /* HTMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLParser.h; sourceTree = ""; }; @@ -354,6 +357,7 @@ 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */, 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */, + 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */, ); name = Nodes; sourceTree = ""; @@ -755,6 +759,7 @@ files = ( 6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, 623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, + 62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */, 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, @@ -806,6 +811,7 @@ files = ( 6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, 623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, + 62EC7AE61AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */, 624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, 6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, 624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, diff --git a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m new file mode 100644 index 0000000..4f69454 --- /dev/null +++ b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m @@ -0,0 +1,373 @@ +// +// HTMLKitMutationAlgorithmsTests.m +// HTMLKit +// +// Created by Iska on 27/04/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLNodes.h" + +@interface HTMLKitMutationAlgorithmsTests : XCTestCase + +@end + +@implementation HTMLKitMutationAlgorithmsTests + +- (void)testValidParentNodeWhenAppending +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + + id parent = [HTMLDocument new]; + XCTAssertNoThrow([parent appendNode:element]); + + parent = [HTMLDocumentFragment new]; + XCTAssertNoThrow([parent appendNode:element]); + + parent = [HTMLElement new]; + XCTAssertNoThrow([parent appendNode:element]); + + parent = [HTMLTemplate new]; + XCTAssertNoThrow([parent appendNode:element]); + + parent = [HTMLDocumentType new]; + XCTAssertThrows([parent appendNode:element]); + + parent = [HTMLComment new]; + XCTAssertThrows([parent appendNode:element]); + + parent = [HTMLText new]; + XCTAssertThrows([parent appendNode:element]); +} + +- (void)testValidParentNodeWhenInserting +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + + id parent = [HTMLDocument new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLDocumentFragment new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLElement new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLTemplate new]; + XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLDocumentType new]; + XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLComment new]; + XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); + + parent = [HTMLText new]; + XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); +} + +- (void)testValidChildNodeWhenInserting +{ + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; + [div appendNode:p]; + + HTMLElement *table = [[HTMLElement alloc] initWithTagName:@"table"]; + + XCTAssertNoThrow([div insertNode:table beforeChildNode:p]); + + [div removeChildNode:p]; + + HTMLElement *section = [[HTMLElement alloc] initWithTagName:@"section"]; + XCTAssertThrows([div insertNode:section beforeChildNode:p]); +} + +- (void)testValidChildNodeWhenReplacing +{ + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; + [div appendNode:p]; + + HTMLElement *table = [[HTMLElement alloc] initWithTagName:@"table"]; + + XCTAssertNoThrow([div replaceChildNode:p withNode:table]); + + XCTAssertThrows([div replaceChildNode:p withNode:[HTMLComment new]]); +} + +- (void)testValidChildNodeWhenRemoving +{ + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; + [div appendNode:p]; + + XCTAssertNoThrow([div removeChildNode:p]); + XCTAssertThrows([div removeChildNode:p]); +} + +- (void)testValidInsertedNode +{ + HTMLDocument *document = [HTMLDocument new]; + + XCTAssertNoThrow([document appendNode:[HTMLDocumentFragment new]]); + XCTAssertNoThrow([document appendNode:[HTMLDocumentType new]]); + XCTAssertNoThrow([document appendNode:[HTMLElement new]]); + + HTMLElement *element = [HTMLElement new]; + + XCTAssertNoThrow([element appendNode:[HTMLTemplate new]]); + XCTAssertNoThrow([element appendNode:[HTMLComment new]]); + XCTAssertNoThrow([element appendNode:[HTMLText new]]); +} + +- (void)testValidParentForDoctype +{ + HTMLDocumentType *doctype = [HTMLDocumentType new]; + + XCTAssertNoThrow([[HTMLDocument new] appendNode:doctype]); + + XCTAssertThrows([[HTMLDocumentFragment new] appendNode:doctype]); + XCTAssertThrows([[HTMLDocumentType new] appendNode:doctype]); + XCTAssertThrows([[HTMLElement new] appendNode:doctype]); + XCTAssertThrows([[HTMLTemplate new] appendNode:doctype]); + XCTAssertThrows([[HTMLComment new] appendNode:doctype]); + XCTAssertThrows([[HTMLText new] appendNode:doctype]); +} + +- (void)testValidParentForText +{ + HTMLText *text = [HTMLText new]; + + XCTAssertThrows([[HTMLDocument new] appendNode:text]); + XCTAssertThrows([[HTMLDocumentType new] appendNode:text]); + XCTAssertThrows([[HTMLComment new] appendNode:text]); + XCTAssertThrows([[HTMLText new] appendNode:text]); + + XCTAssertNoThrow([[HTMLDocumentFragment new] appendNode:text]); + XCTAssertNoThrow([[HTMLElement new] appendNode:text]); + XCTAssertNoThrow([[HTMLTemplate new] appendNode:text]); +} + +- (void)testValidDocumentFragmentInsertionIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:document]; + + void (^ reset)() = ^ { + [fragment removeAllChildNodes]; + [document removeAllChildNodes]; + }; + + /** + * Fragment has a Text node child + */ + [fragment appendNode:[HTMLText new]]; + XCTAssertThrows([document appendNode:fragment]); + + /** + * Fragment has more than one Element child + */ + reset(); + [fragment appendNode:[HTMLElement new]]; + [fragment appendNode:[HTMLElement new]]; + XCTAssertThrows([document appendNode:fragment]); + + + /** + * Fragment has one node child + * Document has an Element child + */ + reset(); + [fragment appendNode:[HTMLElement new]]; + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document appendNode:fragment]); + + /** + * Fragment has one node child + * "before child" is a Doctype + */ + reset(); + HTMLDocumentType *doctype = [HTMLDocumentType new]; + [fragment appendNode:[HTMLElement new]]; + [document appendNode:doctype]; + XCTAssertThrows([document insertNode:fragment beforeChildNode:doctype]); + + /** + * Fragment has one node child + * "before child" is following a Doctype + */ + reset(); + HTMLComment *doctypePreviousSibling = [HTMLComment new]; + [fragment appendNode:[HTMLElement new]]; + [document appendNode:doctypePreviousSibling]; + [document appendNode:doctype]; + XCTAssertThrows([document insertNode:fragment beforeChildNode:doctypePreviousSibling]); +} + +- (void)testValidElementInsertionIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLElement *element = [HTMLElement new]; + + void (^ reset)() = ^ { + [element removeAllChildNodes]; + [document removeAllChildNodes]; + }; + + /** + * Document has an Element child + */ + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document appendNode:element]); + + /** + * "before child" is a Doctype + */ + reset(); + HTMLDocumentType *doctype = [HTMLDocumentType new]; + [document appendNode:doctype]; + XCTAssertThrows([document insertNode:element beforeChildNode:doctype]); + + /** + * Doctype is following the "before child" + */ + reset(); + HTMLComment *doctypePreviousSibling = [HTMLComment new]; + [document appendNode:doctypePreviousSibling]; + [document appendNode:doctype]; + XCTAssertThrows([document insertNode:element beforeChildNode:doctypePreviousSibling]); +} + +- (void)testValidDoctypeInsertionIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLDocumentType *doctype = [HTMLDocumentType new]; + + void (^ reset)() = ^ { + [document removeAllChildNodes]; + }; + + /** + * Document has a Doctype child + */ + [document appendNode:[HTMLDocumentType new]]; + XCTAssertThrows([document appendNode:doctype]); + + /** + * An Element is preceding the "before child" + */ + reset(); + HTMLComment *secondChild = [HTMLComment new]; + [document appendNode:[HTMLElement new]]; + [document appendNode:secondChild]; + XCTAssertThrows([document insertNode:doctype beforeChildNode:secondChild]); + + /** + * Document has an Element child + */ + reset(); + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document appendNode:doctype]); +} + +- (void)testValidDocumentFragmentReplacementIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLComment *child = [HTMLComment new]; + HTMLDocumentFragment *replacement = [[HTMLDocumentFragment alloc] initWithDocument:document]; + + void (^ reset)() = ^ { + [replacement removeAllChildNodes]; + [document removeAllChildNodes]; + [document appendNode:child]; + }; + + /** + * Replacement Fragment has a Text node child + */ + [replacement appendNode:[HTMLText new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * Replacement Fragment has more than one Element child + */ + reset(); + [replacement appendNode:[HTMLElement new]]; + [replacement appendNode:[HTMLElement new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * Replacement Fragment has one Element child + * Document has an Element child that is not the Replacement + */ + reset(); + [replacement appendNode:[HTMLElement new]]; + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * Replacement Fragment has one Element child + * Doctype is following the child node + */ + reset(); + HTMLDocumentType *doctype = [HTMLDocumentType new]; + [replacement appendNode:[HTMLElement new]]; + [document appendNode:doctype]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); +} + +- (void)testValidElementReplacementIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLComment *child = [HTMLComment new]; + HTMLElement *replacement = [HTMLElement new]; + + void (^ reset)() = ^ { + [replacement removeAllChildNodes]; + [document removeAllChildNodes]; + [document appendNode:child]; + }; + + /** + * Docment has an Element child that is not replacement + */ + [document appendNode:[HTMLElement new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * Doctype is following the child node + */ + reset(); + HTMLDocumentType *doctype = [HTMLDocumentType new]; + [document appendNode:doctype]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); +} + +- (void)testValidDoctypeReplacementIntoDocument +{ + HTMLDocument *document = [HTMLDocument new]; + HTMLComment *child = [HTMLComment new]; + HTMLDocumentType *replacement = [HTMLDocumentType new]; + + void (^ reset)() = ^ { + [replacement removeAllChildNodes]; + [document removeAllChildNodes]; + [document appendNode:child]; + }; + + /** + * Docment has an Doctype child that is not replacement + */ + [document appendNode:[HTMLDocumentType new]]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); + + /** + * An Element is preceding the child node + */ + reset(); + [document insertNode:[HTMLElement new] beforeChildNode:child]; + XCTAssertThrows([document replaceChildNode:child withNode:replacement]); +} + +@end diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index 0625c11..56a5d6a 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -403,359 +403,4 @@ XCTAssertEqualObjects(thirdChild.parentNode, newParent); } -- (void)testValidParentNodeWhenAppending -{ - HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; - - id parent = [HTMLDocument new]; - XCTAssertNoThrow([parent appendNode:element]); - - parent = [HTMLDocumentFragment new]; - XCTAssertNoThrow([parent appendNode:element]); - - parent = [HTMLElement new]; - XCTAssertNoThrow([parent appendNode:element]); - - parent = [HTMLTemplate new]; - XCTAssertNoThrow([parent appendNode:element]); - - parent = [HTMLDocumentType new]; - XCTAssertThrows([parent appendNode:element]); - - parent = [HTMLComment new]; - XCTAssertThrows([parent appendNode:element]); - - parent = [HTMLText new]; - XCTAssertThrows([parent appendNode:element]); -} - -- (void)testValidParentNodeWhenInserting -{ - HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; - - id parent = [HTMLDocument new]; - XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); - - parent = [HTMLDocumentFragment new]; - XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); - - parent = [HTMLElement new]; - XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); - - parent = [HTMLTemplate new]; - XCTAssertNoThrow([parent insertNode:element beforeChildNode:nil]); - - parent = [HTMLDocumentType new]; - XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); - - parent = [HTMLComment new]; - XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); - - parent = [HTMLText new]; - XCTAssertThrows([parent insertNode:element beforeChildNode:nil]); -} - -- (void)testValidChildNodeWhenInserting -{ - HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; - HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; - [div appendNode:p]; - - HTMLElement *table = [[HTMLElement alloc] initWithTagName:@"table"]; - - XCTAssertNoThrow([div insertNode:table beforeChildNode:p]); - - [div removeChildNode:p]; - - HTMLElement *section = [[HTMLElement alloc] initWithTagName:@"section"]; - XCTAssertThrows([div insertNode:section beforeChildNode:p]); -} - -- (void)testValidChildNodeWhenReplacing -{ - HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; - HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; - [div appendNode:p]; - - HTMLElement *table = [[HTMLElement alloc] initWithTagName:@"table"]; - - XCTAssertNoThrow([div replaceChildNode:p withNode:table]); - - XCTAssertThrows([div replaceChildNode:p withNode:[HTMLComment new]]); -} - -- (void)testValidChildNodeWhenRemoving -{ - HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div"]; - HTMLElement *p = [[HTMLElement alloc] initWithTagName:@"p"]; - [div appendNode:p]; - - XCTAssertNoThrow([div removeChildNode:p]); - XCTAssertThrows([div removeChildNode:p]); -} - -- (void)testValidInsertedNode -{ - HTMLDocument *document = [HTMLDocument new]; - - XCTAssertNoThrow([document appendNode:[HTMLDocumentFragment new]]); - XCTAssertNoThrow([document appendNode:[HTMLDocumentType new]]); - XCTAssertNoThrow([document appendNode:[HTMLElement new]]); - - HTMLElement *element = [HTMLElement new]; - - XCTAssertNoThrow([element appendNode:[HTMLTemplate new]]); - XCTAssertNoThrow([element appendNode:[HTMLComment new]]); - XCTAssertNoThrow([element appendNode:[HTMLText new]]); -} - -- (void)testValidParentForDoctype -{ - HTMLDocumentType *doctype = [HTMLDocumentType new]; - - XCTAssertNoThrow([[HTMLDocument new] appendNode:doctype]); - - XCTAssertThrows([[HTMLDocumentFragment new] appendNode:doctype]); - XCTAssertThrows([[HTMLDocumentType new] appendNode:doctype]); - XCTAssertThrows([[HTMLElement new] appendNode:doctype]); - XCTAssertThrows([[HTMLTemplate new] appendNode:doctype]); - XCTAssertThrows([[HTMLComment new] appendNode:doctype]); - XCTAssertThrows([[HTMLText new] appendNode:doctype]); -} - -- (void)testValidParentForText -{ - HTMLText *text = [HTMLText new]; - - XCTAssertThrows([[HTMLDocument new] appendNode:text]); - XCTAssertThrows([[HTMLDocumentType new] appendNode:text]); - XCTAssertThrows([[HTMLComment new] appendNode:text]); - XCTAssertThrows([[HTMLText new] appendNode:text]); - - XCTAssertNoThrow([[HTMLDocumentFragment new] appendNode:text]); - XCTAssertNoThrow([[HTMLElement new] appendNode:text]); - XCTAssertNoThrow([[HTMLTemplate new] appendNode:text]); -} - -- (void)testValidDocumentFragmentInsertionIntoDocument -{ - HTMLDocument *document = [HTMLDocument new]; - HTMLDocumentFragment *fragment = [[HTMLDocumentFragment alloc] initWithDocument:document]; - - void (^ reset)() = ^ { - [fragment removeAllChildNodes]; - [document removeAllChildNodes]; - }; - - /** - * Fragment has a Text node child - */ - [fragment appendNode:[HTMLText new]]; - XCTAssertThrows([document appendNode:fragment]); - - /** - * Fragment has more than one Element child - */ - reset(); - [fragment appendNode:[HTMLElement new]]; - [fragment appendNode:[HTMLElement new]]; - XCTAssertThrows([document appendNode:fragment]); - - - /** - * Fragment has one node child - * Document has an Element child - */ - reset(); - [fragment appendNode:[HTMLElement new]]; - [document appendNode:[HTMLElement new]]; - XCTAssertThrows([document appendNode:fragment]); - - /** - * Fragment has one node child - * "before child" is a Doctype - */ - reset(); - HTMLDocumentType *doctype = [HTMLDocumentType new]; - [fragment appendNode:[HTMLElement new]]; - [document appendNode:doctype]; - XCTAssertThrows([document insertNode:fragment beforeChildNode:doctype]); - - /** - * Fragment has one node child - * "before child" is following a Doctype - */ - reset(); - HTMLComment *doctypePreviousSibling = [HTMLComment new]; - [fragment appendNode:[HTMLElement new]]; - [document appendNode:doctypePreviousSibling]; - [document appendNode:doctype]; - XCTAssertThrows([document insertNode:fragment beforeChildNode:doctypePreviousSibling]); -} - -- (void)testValidElementInsertionIntoDocument -{ - HTMLDocument *document = [HTMLDocument new]; - HTMLElement *element = [HTMLElement new]; - - void (^ reset)() = ^ { - [element removeAllChildNodes]; - [document removeAllChildNodes]; - }; - - /** - * Document has an Element child - */ - [document appendNode:[HTMLElement new]]; - XCTAssertThrows([document appendNode:element]); - - /** - * "before child" is a Doctype - */ - reset(); - HTMLDocumentType *doctype = [HTMLDocumentType new]; - [document appendNode:doctype]; - XCTAssertThrows([document insertNode:element beforeChildNode:doctype]); - - /** - * Doctype is following the "before child" - */ - reset(); - HTMLComment *doctypePreviousSibling = [HTMLComment new]; - [document appendNode:doctypePreviousSibling]; - [document appendNode:doctype]; - XCTAssertThrows([document insertNode:element beforeChildNode:doctypePreviousSibling]); -} - -- (void)testValidDoctypeInsertionIntoDocument -{ - HTMLDocument *document = [HTMLDocument new]; - HTMLDocumentType *doctype = [HTMLDocumentType new]; - - void (^ reset)() = ^ { - [document removeAllChildNodes]; - }; - - /** - * Document has a Doctype child - */ - [document appendNode:[HTMLDocumentType new]]; - XCTAssertThrows([document appendNode:doctype]); - - /** - * An Element is preceding the "before child" - */ - reset(); - HTMLComment *secondChild = [HTMLComment new]; - [document appendNode:[HTMLElement new]]; - [document appendNode:secondChild]; - XCTAssertThrows([document insertNode:doctype beforeChildNode:secondChild]); - - /** - * Document has an Element child - */ - reset(); - [document appendNode:[HTMLElement new]]; - XCTAssertThrows([document appendNode:doctype]); -} - -- (void)testValidDocumentFragmentReplacementIntoDocument -{ - HTMLDocument *document = [HTMLDocument new]; - HTMLComment *child = [HTMLComment new]; - HTMLDocumentFragment *replacement = [[HTMLDocumentFragment alloc] initWithDocument:document]; - - void (^ reset)() = ^ { - [replacement removeAllChildNodes]; - [document removeAllChildNodes]; - [document appendNode:child]; - }; - - /** - * Replacement Fragment has a Text node child - */ - [replacement appendNode:[HTMLText new]]; - XCTAssertThrows([document replaceChildNode:child withNode:replacement]); - - /** - * Replacement Fragment has more than one Element child - */ - reset(); - [replacement appendNode:[HTMLElement new]]; - [replacement appendNode:[HTMLElement new]]; - XCTAssertThrows([document replaceChildNode:child withNode:replacement]); - - /** - * Replacement Fragment has one Element child - * Document has an Element child that is not the Replacement - */ - reset(); - [replacement appendNode:[HTMLElement new]]; - [document appendNode:[HTMLElement new]]; - XCTAssertThrows([document replaceChildNode:child withNode:replacement]); - - /** - * Replacement Fragment has one Element child - * Doctype is following the child node - */ - reset(); - HTMLDocumentType *doctype = [HTMLDocumentType new]; - [replacement appendNode:[HTMLElement new]]; - [document appendNode:doctype]; - XCTAssertThrows([document replaceChildNode:child withNode:replacement]); -} - -- (void)testValidElementReplacementIntoDocument -{ - HTMLDocument *document = [HTMLDocument new]; - HTMLComment *child = [HTMLComment new]; - HTMLElement *replacement = [HTMLElement new]; - - void (^ reset)() = ^ { - [replacement removeAllChildNodes]; - [document removeAllChildNodes]; - [document appendNode:child]; - }; - - /** - * Docment has an Element child that is not replacement - */ - [document appendNode:[HTMLElement new]]; - XCTAssertThrows([document replaceChildNode:child withNode:replacement]); - - /** - * Doctype is following the child node - */ - reset(); - HTMLDocumentType *doctype = [HTMLDocumentType new]; - [document appendNode:doctype]; - XCTAssertThrows([document replaceChildNode:child withNode:replacement]); -} - -- (void)testValidDoctypeReplacementIntoDocument -{ - HTMLDocument *document = [HTMLDocument new]; - HTMLComment *child = [HTMLComment new]; - HTMLDocumentType *replacement = [HTMLDocumentType new]; - - void (^ reset)() = ^ { - [replacement removeAllChildNodes]; - [document removeAllChildNodes]; - [document appendNode:child]; - }; - - /** - * Docment has an Doctype child that is not replacement - */ - [document appendNode:[HTMLDocumentType new]]; - XCTAssertThrows([document replaceChildNode:child withNode:replacement]); - - /** - * An Element is preceding the child node - */ - reset(); - [document insertNode:[HTMLElement new] beforeChildNode:child]; - XCTAssertThrows([document replaceChildNode:child withNode:replacement]); -} - @end From bf061d63670177719eba15b717472fcb7768091e Mon Sep 17 00:00:00 2001 From: iska Date: Mon, 27 Apr 2015 19:45:32 +0200 Subject: [PATCH 13/55] Add support for inserting a Document Fragment Previous implementation would just insert the fragment as a child node, whereas it should insert its child nodes. --- HTMLKit/HTMLNode.m | 64 +++++++++++-------- HTMLKitTests/HTMLKitMutationAlgorithmsTests.m | 27 ++++++++ 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 6b211be..d4c51aa 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -139,35 +139,48 @@ return [self.childNodes indexOfObject:node]; } -- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child -{ - node = [self preInsertNode:node beforeChildNode:child]; - node.parentNode = self; - return node; -} - - (HTMLNode *)appendNode:(HTMLNode *)node { - node = [self preInsertNode:node beforeChildNode:nil]; - node.parentNode = self; - return node; + return [self insertNode:node beforeChildNode:nil]; } - (void)appendNodes:(NSArray *)nodes { for (id node in nodes) { - [self appendNode:node]; + [self insertNode:node beforeChildNode:nil]; } } +- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child +{ + [self ensurePreInsertionValidityOfNode:node beforeChildNode:child]; + + [self.ownerDocument adoptNode:node]; + + NSArray *nodes = node.type == HTMLNodeDocumentFragment ? [NSArray arrayWithArray:node.childNodes.array] : @[node]; + + NSUInteger index = [self indexOfChildNode:child]; + if (index != NSNotFound) { + NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, nodes.count)]; + [(NSMutableOrderedSet *)self.childNodes insertObjects:nodes atIndexes:indexes]; + } else { + [(NSMutableOrderedSet *)self.childNodes addObjectsFromArray:nodes]; + } + + if (node.type == HTMLNodeDocumentFragment) { + [node removeAllChildNodes]; + } + + [nodes makeObjectsPerformSelector:@selector(setParentNode:) withObject:self]; + + return node; +} + - (HTMLNode *)replaceChildNode:(HTMLNode *)child withNode:(HTMLNode *)node { [self ensureReplacementValidityOfChildNode:child withNode:node]; - [self.ownerDocument adoptNode:node]; - NSUInteger index = [self indexOfChildNode:child]; - node.parentNode = self; - [(NSMutableOrderedSet *)self.childNodes replaceObjectAtIndex:index withObject:node]; + [self insertNode:node beforeChildNode:child]; [child removeFromParentNode]; return child; } @@ -216,7 +229,8 @@ - (void)removeAllChildNodes { - [self.childNodes.array makeObjectsPerformSelector:@selector(removeFromParentNode)]; + [self.childNodes.array makeObjectsPerformSelector:@selector(setParentNode:) withObject:nil]; + [(NSMutableOrderedSet *)self.childNodes removeAllObjects]; } #pragma mark - Enumeration @@ -242,22 +256,22 @@ return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:YES]; } -#pragma mark - Mutation Algorithms - -- (HTMLNode *)preInsertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child +- (void)doInsertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child { - [self ensurePreInsertionValidityOfNode:node beforeChildNode:child]; - [self.ownerDocument adoptNode:node]; + NSArray *nodes = node.type == HTMLNodeDocumentFragment ? node.childNodes.array : @[node]; + NSUInteger index = [self indexOfChildNode:child]; if (index != NSNotFound) { - [(NSMutableOrderedSet *)self.childNodes insertObject:node atIndex:index]; + NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, nodes.count)]; + [(NSMutableOrderedSet *)self.childNodes insertObjects:nodes atIndexes:indexes]; } else { - [(NSMutableOrderedSet *)self.childNodes addObject:node]; + [(NSMutableOrderedSet *)self.childNodes addObjectsFromArray:nodes]; } - - return node; } + +#pragma mark - Validity Checks + NS_INLINE void CheckParentValid(HTMLNode *parent, NSString *cmd) { if (parent.type != HTMLNodeDocument && diff --git a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m index 4f69454..d4d37f6 100644 --- a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m +++ b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m @@ -8,6 +8,9 @@ #import #import "HTMLNodes.h" +#import "NSString+HTMLKit.h" + +extern uint64_t dispatch_benchmark(size_t count, void (^block)(void)); @interface HTMLKitMutationAlgorithmsTests : XCTestCase @@ -15,6 +18,30 @@ @implementation HTMLKitMutationAlgorithmsTests +- (void)testAppendDocumentFragment +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLComment *comment = [HTMLComment new]; + [element appendNode:comment]; + + HTMLDocumentFragment *fragment = [HTMLDocumentFragment new]; + HTMLElement *firstChild = [HTMLElement new]; + HTMLElement *secondChild = [HTMLElement new]; + [fragment appendNode:firstChild]; + [fragment appendNode:secondChild]; + + [element appendNode:fragment]; + XCTAssertEqual(element.childNodesCount, 3); + XCTAssertEqual(fragment.childNodesCount, 0); + + XCTAssertEqualObjects(firstChild.parentNode, element); + XCTAssertEqualObjects(secondChild.parentNode, element); + XCTAssertEqualObjects(element.firstChiledNode, comment); + XCTAssertEqualObjects(element.firstChiledNode.nextSibling, firstChild); + XCTAssertEqualObjects(element.lastChildNode.previousSibling, firstChild); + XCTAssertEqualObjects(element.lastChildNode, secondChild); +} + - (void)testValidParentNodeWhenAppending { HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; From 3d3e4d52b46d98cca524509a386a69e5dee1ed2a Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 2 May 2015 20:52:03 +0200 Subject: [PATCH 14/55] Add a NO_DOM_CHECKS macro to disable validations for DOM manipulations DOM validation checks are expensive. This macro would allow for removing these checks from the compiled product, which would increase performance by about 20-30%. However, the user is responsible for the validity of the manipulations and the resulting DOM. --- HTMLKit/HTMLNode.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index d4c51aa..6aac8c2 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -153,7 +153,9 @@ - (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child { +#ifndef HTMLKIT_NO_DOM_CHECKS [self ensurePreInsertionValidityOfNode:node beforeChildNode:child]; +#endif [self.ownerDocument adoptNode:node]; @@ -178,7 +180,9 @@ - (HTMLNode *)replaceChildNode:(HTMLNode *)child withNode:(HTMLNode *)node { +#ifndef HTMLKIT_NO_DOM_CHECKS [self ensureReplacementValidityOfChildNode:child withNode:node]; +#endif [self insertNode:node beforeChildNode:child]; [child removeFromParentNode]; @@ -269,6 +273,7 @@ } } +#ifndef HTMLKIT_NO_DOM_CHECKS #pragma mark - Validity Checks @@ -431,6 +436,8 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin } } +#endif + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone From 2c9f68e2ea486ff1fae3a256bcc13d05a5c8881f Mon Sep 17 00:00:00 2001 From: iska Date: Sat, 2 May 2015 21:08:21 +0200 Subject: [PATCH 15/55] Add Node methods to prepend nodes --- HTMLKit/HTMLNode.h | 6 +- HTMLKit/HTMLNode.m | 12 ++++ HTMLKitTests/HTMLKitMutationAlgorithmsTests.m | 24 ------- HTMLKitTests/HTMLKitNodesTests.m | 67 +++++++++++++++++++ 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index b07c56f..332fb4f 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -67,12 +67,16 @@ typedef NS_ENUM(short, HTMLNodeType) - (NSUInteger)indexOfChildNode:(HTMLNode *)node; -- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child; +- (HTMLNode *)prependNode:(HTMLNode *)node; + +- (void)prependNodes:(NSArray *)nodes; - (HTMLNode *)appendNode:(HTMLNode *)node; - (void)appendNodes:(NSArray *)nodes; +- (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child; + - (HTMLNode *)replaceChildNode:(HTMLNode *)node withNode:(HTMLNode *)replacement; - (void)replaceAllChildNodesWithNode:(HTMLNode *)node; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 6aac8c2..470cc15 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -139,6 +139,18 @@ return [self.childNodes indexOfObject:node]; } +- (HTMLNode *)prependNode:(HTMLNode *)node +{ + return [self insertNode:node beforeChildNode:self.firstChiledNode]; +} + +- (void)prependNodes:(NSArray *)nodes +{ + for (id node in nodes.reverseObjectEnumerator) { + [self insertNode:node beforeChildNode:self.firstChiledNode]; + } +} + - (HTMLNode *)appendNode:(HTMLNode *)node { return [self insertNode:node beforeChildNode:nil]; diff --git a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m index d4d37f6..990d574 100644 --- a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m +++ b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m @@ -18,30 +18,6 @@ extern uint64_t dispatch_benchmark(size_t count, void (^block)(void)); @implementation HTMLKitMutationAlgorithmsTests -- (void)testAppendDocumentFragment -{ - HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; - HTMLComment *comment = [HTMLComment new]; - [element appendNode:comment]; - - HTMLDocumentFragment *fragment = [HTMLDocumentFragment new]; - HTMLElement *firstChild = [HTMLElement new]; - HTMLElement *secondChild = [HTMLElement new]; - [fragment appendNode:firstChild]; - [fragment appendNode:secondChild]; - - [element appendNode:fragment]; - XCTAssertEqual(element.childNodesCount, 3); - XCTAssertEqual(fragment.childNodesCount, 0); - - XCTAssertEqualObjects(firstChild.parentNode, element); - XCTAssertEqualObjects(secondChild.parentNode, element); - XCTAssertEqualObjects(element.firstChiledNode, comment); - XCTAssertEqualObjects(element.firstChiledNode.nextSibling, firstChild); - XCTAssertEqualObjects(element.lastChildNode.previousSibling, firstChild); - XCTAssertEqualObjects(element.lastChildNode, secondChild); -} - - (void)testValidParentNodeWhenAppending { HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index 56a5d6a..e2b65cc 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -44,6 +44,73 @@ XCTAssertNil(node.lastChildNode); } +- (void)testAppendNode +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLComment *comment = [HTMLComment new]; + [element appendNode:comment]; + + XCTAssertEqual(element.childNodesCount, 1); + XCTAssertEqual(element.firstChiledNode, comment); + + HTMLElement *firstElement = [HTMLElement new]; + HTMLElement *secondElement = [HTMLElement new]; + NSArray *nodes = @[firstElement, secondElement]; + + [element appendNodes:nodes]; + + XCTAssertEqual(element.childNodesCount, 3); + XCTAssertEqual(element.firstChiledNode, comment); + XCTAssertEqual(element.lastChildNode, secondElement); +} + +- (void)testPrependNode +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLText *text = [HTMLText new]; + [element appendNode:text]; + + HTMLComment *comment = [HTMLComment new]; + [element prependNode:comment]; + + XCTAssertEqual(element.childNodesCount, 2); + XCTAssertEqual(element.firstChiledNode, comment); + + HTMLElement *firstElement = [HTMLElement new]; + HTMLElement *secondElement = [HTMLElement new]; + NSArray *nodes = @[firstElement, secondElement]; + + [element prependNodes:nodes]; + + XCTAssertEqual(element.childNodesCount, 4); + XCTAssertEqual(element.firstChiledNode, firstElement); + XCTAssertEqual(element.lastChildNode, text); +} + +- (void)testAppendDocumentFragment +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + HTMLComment *comment = [HTMLComment new]; + [element appendNode:comment]; + + HTMLDocumentFragment *fragment = [HTMLDocumentFragment new]; + HTMLElement *firstChild = [HTMLElement new]; + HTMLElement *secondChild = [HTMLElement new]; + [fragment appendNode:firstChild]; + [fragment appendNode:secondChild]; + + [element appendNode:fragment]; + XCTAssertEqual(element.childNodesCount, 3); + XCTAssertEqual(fragment.childNodesCount, 0); + + XCTAssertEqualObjects(firstChild.parentNode, element); + XCTAssertEqualObjects(secondChild.parentNode, element); + XCTAssertEqualObjects(element.firstChiledNode, comment); + XCTAssertEqualObjects(element.firstChiledNode.nextSibling, firstChild); + XCTAssertEqualObjects(element.lastChildNode.previousSibling, firstChild); + XCTAssertEqualObjects(element.lastChildNode, secondChild); +} + - (void)testParentNode { HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; From 111acb4f8fd2e860657f3c10388b3f47ef394acb Mon Sep 17 00:00:00 2001 From: iska Date: Wed, 20 May 2015 22:39:52 +0200 Subject: [PATCH 16/55] Add a convenience HTML Document initializer Creates a document directly given a HTML string, instead of manually creating a parser. --- HTMLKit/HTMLDocument.h | 2 ++ HTMLKit/HTMLDocument.m | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/HTMLKit/HTMLDocument.h b/HTMLKit/HTMLDocument.h index ddcec2b..7c0655f 100644 --- a/HTMLKit/HTMLDocument.h +++ b/HTMLKit/HTMLDocument.h @@ -27,6 +27,8 @@ typedef NS_ENUM(short, HTMLDocumentReadyState) @property (nonatomic, assign, readonly) HTMLDocumentReadyState readyState; ++ (instancetype)documentWithString:(NSString *)string; + - (HTMLNode *)adoptNode:(HTMLNode *)node; - (HTMLDocument *)associatedInertTemplateDocument; diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 11e2a42..8a5329f 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -7,6 +7,7 @@ // #import "HTMLDocument.h" +#import "HTMLParser.h" #import "HTMLKitExceptions.h" @interface HTMLNode (Private) @@ -25,6 +26,12 @@ #pragma mark - Init ++ (instancetype)documentWithString:(NSString *)string +{ + HTMLParser *parser = [[HTMLParser alloc] initWithString:string]; + return [parser parseDocument]; +} + - (instancetype)init { self = [super initWithName:@"#document" type:HTMLNodeDocument]; From 74aec5778536420c8071cfdd996a73b182294680 Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 21 May 2015 00:20:20 +0200 Subject: [PATCH 17/55] Add method to set inner HTML of an Element --- HTMLKit/HTMLElement.m | 9 +++++++++ HTMLKit/HTMLNode.h | 10 ++++++---- HTMLKit/HTMLNode.m | 12 ++++++++++++ HTMLKitTests/HTMLKitNodesTests.m | 13 +++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/HTMLKit/HTMLElement.m b/HTMLKit/HTMLElement.m index e12b5f7..11f0138 100644 --- a/HTMLKit/HTMLElement.m +++ b/HTMLKit/HTMLElement.m @@ -7,6 +7,7 @@ // #import "HTMLElement.h" +#import "HTMLParser.h" #import "HTMLDocument.h" #import "HTMLText.h" @@ -101,6 +102,14 @@ [self replaceAllChildNodesWithNode:node]; } +- (void)setInnerHTML:(NSString *)innerHTML +{ + HTMLParser *parser = [[HTMLParser alloc] initWithString:innerHTML]; + NSArray *fragmentNodes = [parser parseFragmentWithContextElement:self]; + [self removeAllChildNodes]; + [self appendNodes:fragmentNodes]; +} + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index 332fb4f..f0d4c25 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -53,10 +53,16 @@ typedef NS_ENUM(short, HTMLNodeType) @property (nonatomic, copy) NSString *textContent; +@property (nonatomic, strong, readonly) NSString *outerHTML; + +@property (nonatomic, copy) NSString *innerHTML; + - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithName:(NSString *)name type:(HTMLNodeType)type; +- (HTMLElement *)asElement; + - (BOOL)hasChildNodes; - (BOOL)hasChildNodeOfType:(HTMLNodeType)type; @@ -97,10 +103,6 @@ typedef NS_ENUM(short, HTMLNodeType) - (NSEnumerator *)reverseTreeEnumerator; -- (NSString *)outerHTML; - -- (NSString *)innerHTML; - - (NSString *)treeDescription; @end diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 470cc15..cc4f739 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -104,6 +104,13 @@ return nil; } +#pragma mark - Cast + +- (HTMLElement *)asElement +{ + return (HTMLElement *)self; +} + #pragma mark - Child Nodes - (BOOL)hasChildNodes @@ -471,6 +478,11 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin return [[self.childNodes.array valueForKey:@"outerHTML"] componentsJoinedByString:@""]; } +- (void)setInnerHTML:(NSString *)outerHTML +{ + [self doesNotRecognizeSelector:_cmd]; +} + #pragma mark - Description - (NSString *)treeDescription diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index e2b65cc..5bb051a 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -470,4 +470,17 @@ XCTAssertEqualObjects(thirdChild.parentNode, newParent); } +- (void)testElementSetInnerHTML +{ + HTMLElement *element = [[HTMLElement alloc] initWithTagName:@"div"]; + [element appendNode:[[HTMLElement alloc] initWithTagName:@"img"]]; + + [element setInnerHTML:@"

"]; + + XCTAssertEqual(element.childNodesCount, 2); + + XCTAssertEqualObjects(element.firstChiledNode.asElement.tagName, @"p"); + XCTAssertEqualObjects([element childNodeAtIndex:1].asElement.tagName, @"p"); +} + @end From 1e9118078f655d1f07e6b61c426a13ad4b0680cf Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 21 May 2015 23:24:13 +0200 Subject: [PATCH 18/55] Rename Nodes group to DOM --- HTMLKit.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index e00d8ff..bcc70a6 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -313,7 +313,7 @@ name = Tokenizer; sourceTree = ""; }; - 623719441AA1472B002E03C8 /* Nodes */ = { + 623719441AA1472B002E03C8 /* DOM */ = { isa = PBXGroup; children = ( 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */, @@ -337,7 +337,7 @@ 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */, 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */, ); - name = Nodes; + name = DOM; sourceTree = ""; }; 623975581AC362A5007E26F1 /* Tree Construction */ = { @@ -421,7 +421,7 @@ 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */, 62AE593219F97CCA0043F069 /* Tokenizing */, 628E16EC1ADAE71700B15A06 /* Parsing */, - 623719441AA1472B002E03C8 /* Nodes */, + 623719441AA1472B002E03C8 /* DOM */, 628E16ED1ADAE73700B15A06 /* Categories */, 628E16EE1ADAE75300B15A06 /* Structures */, 625A14B619C7829400AD0C32 /* Supporting Files */, From 2ee3b2d2b3e763c741703f87b50f29c6eb8089c2 Mon Sep 17 00:00:00 2001 From: iska Date: Fri, 22 May 2015 20:06:24 +0200 Subject: [PATCH 19/55] Rename HTMLNode header to HTMLDOM --- HTMLKit.xcodeproj/project.pbxproj | 10 +++++----- HTMLKit/{HTMLNodes.h => HTMLDOM.h} | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename HTMLKit/{HTMLNodes.h => HTMLDOM.h} (100%) diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index bcc70a6..8490df7 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -118,7 +118,7 @@ 62AE594B19F9948A0043F069 /* HTMLCharacterToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */; }; 62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; }; 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; }; - 62D89DB01AE7080300B6243D /* HTMLNodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */; }; + 62D89DB01AE7080300B6243D /* HTMLDOM.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3F1A9FDE8A00301989 /* HTMLDOM.h */; }; 62D89DB11AE7081600B6243D /* HTMLTokenizerStates.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */; }; 62D89DB21AE7081600B6243D /* HTMLTokenizerCharacters.h in Headers */ = {isa = PBXBuildFile; fileRef = 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */; }; 62D89DB31AE7081600B6243D /* HTMLNamespaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */; }; @@ -185,7 +185,7 @@ 6235CE9F1AA5170A0026937B /* HTMLMarker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLMarker.m; sourceTree = ""; }; 62362A3A1A9FA70400301989 /* HTMLText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLText.h; sourceTree = ""; }; 62362A3B1A9FA70400301989 /* HTMLText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLText.m; sourceTree = ""; }; - 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLNodes.h; sourceTree = ""; }; + 62362A3F1A9FDE8A00301989 /* HTMLDOM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLDOM.h; sourceTree = ""; }; 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLListOfActiveFormattingElements.h; sourceTree = ""; }; 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLListOfActiveFormattingElements.m; sourceTree = ""; }; 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTokenizerPerformance.m; sourceTree = ""; }; @@ -316,8 +316,7 @@ 623719441AA1472B002E03C8 /* DOM */ = { isa = PBXGroup; children = ( - 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */, - 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */, + 62362A3F1A9FDE8A00301989 /* HTMLDOM.h */, 6234584C1A9D2FA4009BD491 /* HTMLNode.h */, 6234584D1A9D2FA4009BD491 /* HTMLNode.m */, 6279F87219E1808D00F12EE5 /* HTMLElement.h */, @@ -336,6 +335,7 @@ 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */, 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */, 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */, + 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */, ); name = DOM; sourceTree = ""; @@ -546,7 +546,7 @@ 62D89DB51AE7081600B6243D /* HTMLElementAdjustment.h in Headers */, 62D89DB61AE7081600B6243D /* HTMLParserInsertionModes.h in Headers */, 62D89DB71AE7081600B6243D /* HTMLQuirksMode.h in Headers */, - 62D89DB01AE7080300B6243D /* HTMLNodes.h in Headers */, + 62D89DB01AE7080300B6243D /* HTMLDOM.h in Headers */, 62F873EB19E088C90062683C /* HTMLParser.h in Headers */, 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */, 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */, diff --git a/HTMLKit/HTMLNodes.h b/HTMLKit/HTMLDOM.h similarity index 100% rename from HTMLKit/HTMLNodes.h rename to HTMLKit/HTMLDOM.h From 11af0509a3efe958c68d6de86b04e6ef195f5718 Mon Sep 17 00:00:00 2001 From: iska Date: Wed, 27 May 2015 00:33:09 +0200 Subject: [PATCH 20/55] Rename type property to nodeType in HTMLNode for better clarity --- HTMLKit/HTMLDOM.h | 2 +- HTMLKit/HTMLDocument.m | 2 +- HTMLKit/HTMLDocumentFragment.m | 2 +- HTMLKit/HTMLElement.m | 4 +- HTMLKit/HTMLNode.h | 2 +- HTMLKit/HTMLNode.m | 60 ++++++++++----------- HTMLKit/HTMLParser.m | 8 +-- HTMLKitTests/HTMLKitNodesTests.m | 2 +- HTMLKitTests/HTMLKitTreeConstructionTests.m | 10 ++-- 9 files changed, 46 insertions(+), 46 deletions(-) diff --git a/HTMLKit/HTMLDOM.h b/HTMLKit/HTMLDOM.h index f0ffa0e..4e9d5ce 100644 --- a/HTMLKit/HTMLDOM.h +++ b/HTMLKit/HTMLDOM.h @@ -9,8 +9,8 @@ #import "HTMLNode.h" #import "HTMLDocument.h" #import "HTMLDocumentType.h" +#import "HTMLDocumentFragment.h" #import "HTMLElement.h" #import "HTMLComment.h" #import "HTMLText.h" #import "HTMLTemplate.h" -#import "HTMLDocumentFragment.h" diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 8a5329f..7ae4e68 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -72,7 +72,7 @@ return nil; } - if (node.type == HTMLNodeDocument) { + if (node.nodeType == HTMLNodeDocument) { [NSException raise:HTMLKitNotSupportedError format:@"%@: Not Fount Error, adopting a document node. The operation is not supported.", NSStringFromSelector(_cmd)]; } diff --git a/HTMLKit/HTMLDocumentFragment.m b/HTMLKit/HTMLDocumentFragment.m index 6b549a8..adcde5e 100644 --- a/HTMLKit/HTMLDocumentFragment.m +++ b/HTMLKit/HTMLDocumentFragment.m @@ -33,7 +33,7 @@ { NSMutableString *content = [NSMutableString string]; for (HTMLNode *node in self.treeEnumerator) { - if (node.type == HTMLNodeText) { + if (node.nodeType == HTMLNodeText) { [content appendString:[(HTMLText *)node data]]; } } diff --git a/HTMLKit/HTMLElement.m b/HTMLKit/HTMLElement.m index 11f0138..8e9a18f 100644 --- a/HTMLKit/HTMLElement.m +++ b/HTMLKit/HTMLElement.m @@ -89,7 +89,7 @@ { NSMutableString *content = [NSMutableString string]; for (HTMLNode *node in self.treeEnumerator) { - if (node.type == HTMLNodeText) { + if (node.nodeType == HTMLNodeText) { [content appendString:[(HTMLText *)node data]]; } } @@ -146,7 +146,7 @@ return result; } - if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChiledNode.type == HTMLNodeText) { + if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChiledNode.nodeType == HTMLNodeText) { HTMLText *textNode = (HTMLText *)self.firstChiledNode; if ([textNode.data hasPrefix:@"\n"]) { [result appendString:@"\n"]; diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index f0d4c25..3c60f57 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -29,7 +29,7 @@ typedef NS_ENUM(short, HTMLNodeType) @interface HTMLNode : NSObject -@property (nonatomic, assign, readonly) HTMLNodeType type; +@property (nonatomic, assign, readonly) HTMLNodeType nodeType; @property (nonatomic, strong, readonly) NSString *name; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index cc4f739..f0060f5 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -32,7 +32,7 @@ self = [super init]; if (self) { _name = name; - _type = type; + _nodeType = type; _childNodes = [NSMutableOrderedSet new]; } return self; @@ -42,7 +42,7 @@ - (HTMLDocument *)ownerDocument { - if (_type == HTMLNodeDocument) { + if (_nodeType == HTMLNodeDocument) { return (HTMLDocument *)self; } else { return _ownerDocument; @@ -68,7 +68,7 @@ - (HTMLElement *)parentElement { - return _parentNode.type == HTMLNodeElement ? (HTMLElement *)_parentNode : nil; + return _parentNode.nodeType == HTMLNodeElement ? (HTMLElement *)_parentNode : nil; } - (HTMLNode *)firstChiledNode @@ -121,7 +121,7 @@ - (BOOL)hasChildNodeOfType:(HTMLNodeType)type { NSUInteger index = [self.childNodes indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { - if ([(HTMLNode *)obj type] == type) { + if ([(HTMLNode *)obj nodeType] == type) { *stop = YES; return YES; } @@ -178,7 +178,7 @@ [self.ownerDocument adoptNode:node]; - NSArray *nodes = node.type == HTMLNodeDocumentFragment ? [NSArray arrayWithArray:node.childNodes.array] : @[node]; + NSArray *nodes = node.nodeType == HTMLNodeDocumentFragment ? [NSArray arrayWithArray:node.childNodes.array] : @[node]; NSUInteger index = [self indexOfChildNode:child]; if (index != NSNotFound) { @@ -188,7 +188,7 @@ [(NSMutableOrderedSet *)self.childNodes addObjectsFromArray:nodes]; } - if (node.type == HTMLNodeDocumentFragment) { + if (node.nodeType == HTMLNodeDocumentFragment) { [node removeAllChildNodes]; } @@ -281,7 +281,7 @@ - (void)doInsertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child { - NSArray *nodes = node.type == HTMLNodeDocumentFragment ? node.childNodes.array : @[node]; + NSArray *nodes = node.nodeType == HTMLNodeDocumentFragment ? node.childNodes.array : @[node]; NSUInteger index = [self indexOfChildNode:child]; if (index != NSNotFound) { @@ -298,9 +298,9 @@ NS_INLINE void CheckParentValid(HTMLNode *parent, NSString *cmd) { - if (parent.type != HTMLNodeDocument && - parent.type != HTMLNodeDocumentFragment && - parent.type != HTMLNodeElement) { + if (parent.nodeType != HTMLNodeDocument && + parent.nodeType != HTMLNodeDocumentFragment && + parent.nodeType != HTMLNodeElement) { [NSException raise:HTMLKitHierarchyRequestError format:@"%@: Hierarchy Request Error, inserting into %@ is not allowed. The operation would yield an incorrect node tree.", cmd, parent.name]; @@ -319,11 +319,11 @@ NS_INLINE void CheckChildsParent(HTMLNode *parent, HTMLNode *child, NSString *cm NS_INLINE void CheckInsertedNodeValid(HTMLNode *node, NSString *cmd) { - if (node.type != HTMLNodeDocumentFragment && - node.type != HTMLNodeDocumentType && - node.type != HTMLNodeElement && - node.type != HTMLNodeText && - node.type != HTMLNodeComment) { + if (node.nodeType != HTMLNodeDocumentFragment && + node.nodeType != HTMLNodeDocumentType && + node.nodeType != HTMLNodeElement && + node.nodeType != HTMLNodeText && + node.nodeType != HTMLNodeComment) { [NSException raise:HTMLKitHierarchyRequestError format:@"%@: Hierarchy Request Error, inserting a %@ is not allowed. The operation would yield an incorrect node tree.", cmd, node.name]; @@ -332,13 +332,13 @@ NS_INLINE void CheckInsertedNodeValid(HTMLNode *node, NSString *cmd) NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSString *cmd) { - if (node.type == HTMLNodeText && parent.type == HTMLNodeDocument) { + if (node.nodeType == HTMLNodeText && parent.nodeType == HTMLNodeDocument) { [NSException raise:HTMLKitHierarchyRequestError format:@"%@: Hierarchy Request Error, inserting a text node %@ into docuement is not allowed. The operation would yield an incorrect node tree.", cmd, parent.name]; } - if (node.type == HTMLNodeDocumentType && parent.type != HTMLNodeDocument) { + if (node.nodeType == HTMLNodeDocumentType && parent.nodeType != HTMLNodeDocument) { [NSException raise:HTMLKitHierarchyRequestError format:@"%@: Hierarchy Request Error, inserting a doctype %@ into a non-document node is not allowed. The operation would yield an incorrect node tree.", cmd, parent.name]; @@ -361,24 +361,24 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin NSStringFromSelector(_cmd), self, node]; }; - if (self.type == HTMLNodeDocument) { - switch (node.type) { + if (self.nodeType == HTMLNodeDocument) { + switch (node.nodeType) { case HTMLNodeDocumentFragment: if (node.childNodesCount > 1 || [node hasChildNodeOfType:HTMLNodeText]) { hierarchyError(); } else if (node.childNodesCount == 1) { if ([self hasChildNodeOfType:HTMLNodeElement] || - child.type == HTMLNodeDocumentType || - child.nextSibling.type == HTMLNodeDocumentType) { + child.nodeType == HTMLNodeDocumentType || + child.nextSibling.nodeType == HTMLNodeDocumentType) { hierarchyError(); } } break; case HTMLNodeElement: if ([self hasChildNodeOfType:HTMLNodeElement] || - child.type == HTMLNodeDocumentType || - (child != nil && child.nextSibling.type == HTMLNodeDocumentType)) { + child.nodeType == HTMLNodeDocumentType || + (child != nil && child.nextSibling.nodeType == HTMLNodeDocumentType)) { hierarchyError(); } break; @@ -413,21 +413,21 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin void (^ checkParentHasAnotherChildOfType)(HTMLNodeType) = ^ void (HTMLNodeType type) { [self enumerateChildNodesUsingBlock:^(HTMLNode *node, NSUInteger idx, BOOL *stop) { - if (node.type == type && node != child) { + if (node.nodeType == type && node != child) { *stop = YES; hierarchyError(); } }]; }; - if (self.type == HTMLNodeDocument) { - switch (node.type) { + if (self.nodeType == HTMLNodeDocument) { + switch (node.nodeType) { case HTMLNodeDocumentFragment: if (node.childNodesCount > 1 || [node hasChildNodeOfType:HTMLNodeText]) { hierarchyError(); } else if (node.childNodesCount == 1) { - if (child.nextSibling.type == HTMLNodeDocumentType) { + if (child.nextSibling.nodeType == HTMLNodeDocumentType) { hierarchyError(); } checkParentHasAnotherChildOfType(HTMLNodeElement); @@ -435,7 +435,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin break; case HTMLNodeElement: { - if (child.nextSibling.type == HTMLNodeDocumentType) { + if (child.nextSibling.nodeType == HTMLNodeDocumentType) { hierarchyError(); } checkParentHasAnotherChildOfType(HTMLNodeElement); @@ -443,7 +443,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin } case HTMLNodeDocumentType: { - if (child.previousSibling.type == HTMLNodeElement) { + if (child.previousSibling.nodeType == HTMLNodeElement) { hierarchyError(); } checkParentHasAnotherChildOfType(HTMLNodeDocumentType); @@ -461,7 +461,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin - (id)copyWithZone:(NSZone *)zone { - HTMLNode *copy = [[self.class alloc] initWithName:self.name type:self.type]; + HTMLNode *copy = [[self.class alloc] initWithName:self.name type:self.nodeType]; return copy; } diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m index f5f0ed5..539ce36 100644 --- a/HTMLKit/HTMLParser.m +++ b/HTMLKit/HTMLParser.m @@ -12,7 +12,7 @@ #import "HTMLStackOfOpenElements.h" #import "HTMLListOfActiveFormattingElements.h" #import "HTMLParserInsertionModes.h" -#import "HTMLNodes.h" +#import "HTMLDOM.h" #import "HTMLElementTypes.h" #import "HTMLElementAdjustment.h" #import "HTMLMarker.h" @@ -452,10 +452,10 @@ HTMLElement *child = nil; HTMLNode *adjustedInsertionLocation = [self appropriatePlaceForInsertingANodeWithOverrideTarget:nil beforeChildNode:&child]; - if (adjustedInsertionLocation.type != HTMLNodeDocument) { - if (child != nil && child.previousSibling.type == HTMLNodeText) { + if (adjustedInsertionLocation.nodeType != HTMLNodeDocument) { + if (child != nil && child.previousSibling.nodeType == HTMLNodeText) { [(HTMLText *)child.previousSibling appendString:data]; - } else if (adjustedInsertionLocation.lastChildNode.type == HTMLNodeText) { + } else if (adjustedInsertionLocation.lastChildNode.nodeType == HTMLNodeText) { [(HTMLText *)adjustedInsertionLocation.lastChildNode appendString:data]; } else { HTMLText *text = [[HTMLText alloc] initWithData:data]; diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index 5bb051a..8e499de 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -30,7 +30,7 @@ HTMLNode *node = [[HTMLNode alloc] initWithName:@"name" type:HTMLNodeElement]; XCTAssertNotNil(node); XCTAssertEqualObjects(node.name, @"name"); - XCTAssertEqual(node.type, HTMLNodeElement); + XCTAssertEqual(node.nodeType, HTMLNodeElement); XCTAssertNotNil(node.childNodes); XCTAssertEqual(node.childNodes.count, 0); diff --git a/HTMLKitTests/HTMLKitTreeConstructionTests.m b/HTMLKitTests/HTMLKitTreeConstructionTests.m index cfb7430..ca1f223 100644 --- a/HTMLKitTests/HTMLKitTreeConstructionTests.m +++ b/HTMLKitTests/HTMLKitTreeConstructionTests.m @@ -9,7 +9,7 @@ #import #import "HTML5LibTreeConstructionTest.h" -#import "HTMLNodes.h" +#import "HTMLDOM.h" #import "HTMLParser.h" @@ -121,14 +121,14 @@ { XCTAssertEqualObjects(actual.name, expected.name, @"Node name mismatch [%@ should be %@]:\n%@", actual.name, expected.name, message); - XCTAssert(actual.type == expected.type, @"Node type mismatch [%hd should be %hd]:\n%@", - actual.type, expected.type, message); + XCTAssert(actual.nodeType == expected.nodeType, @"Node type mismatch [%hd should be %hd]:\n%@", + actual.nodeType, expected.nodeType, message); - if (actual.type != expected.type) { + if (actual.nodeType != expected.nodeType) { return; } - switch (actual.type) { + switch (actual.nodeType) { case HTMLNodeDocumentType: XCTAssertEqualObjects([(HTMLDocumentType *)actual publicIdentifier], [(HTMLDocumentType *)expected publicIdentifier], @"%@", message); XCTAssertEqualObjects([(HTMLDocumentType *)actual systemIdentifier], [(HTMLDocumentType *)expected systemIdentifier], @"%@", message); From 915f4cc064904fa9f3fa92998d6689f081027ddd Mon Sep 17 00:00:00 2001 From: iska Date: Wed, 27 May 2015 00:38:07 +0200 Subject: [PATCH 21/55] Remove baseURI property from HTMLNode Proper support will be implemented when needed. --- HTMLKit/HTMLNode.h | 2 -- HTMLKit/HTMLNode.m | 6 ------ HTMLKitTests/HTMLKitNodesTests.m | 3 +-- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index 3c60f57..916ed3e 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -35,8 +35,6 @@ typedef NS_ENUM(short, HTMLNodeType) @property (nonatomic, weak, readonly) HTMLDocument *ownerDocument; -@property (nonatomic, strong, readonly) NSString *baseURI; - @property (nonatomic, weak, readonly) HTMLNode *parentNode; @property (nonatomic, weak, readonly) HTMLElement *parentElement; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index f0060f5..363424d 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -55,12 +55,6 @@ [self.childNodes.array makeObjectsPerformSelector:@selector(setOwnerDocument:) withObject:ownerDocument]; } -- (void)setBaseURI:(NSString *)baseURI -{ - _baseURI = [baseURI copy]; - [self.childNodes.array makeObjectsPerformSelector:@selector(setBaseURI:) withObject:baseURI]; -} - - (void)setParentNode:(HTMLNode *)parentNode { _parentNode = parentNode; diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index 8e499de..524a25c 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -7,7 +7,7 @@ // #import -#import "HTMLNodes.h" +#import "HTMLDOM.h" @interface HTMLKitNodesTests : XCTestCase @@ -35,7 +35,6 @@ XCTAssertEqual(node.childNodes.count, 0); XCTAssertNil(node.ownerDocument); - XCTAssertNil(node.baseURI); XCTAssertNil(node.parentNode); XCTAssertNil(node.parentElement); XCTAssertNil(node.firstChiledNode); From 470b9b8d3739be4b846a08691e4ff7a981106590 Mon Sep 17 00:00:00 2001 From: iska Date: Wed, 27 May 2015 00:39:39 +0200 Subject: [PATCH 22/55] Change arguments order in HTML Element initializer for convenience Dictionary argument moved to last position. --- HTMLKit/HTMLElement.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HTMLKit/HTMLElement.h b/HTMLKit/HTMLElement.h index 1085162..6343516 100644 --- a/HTMLKit/HTMLElement.h +++ b/HTMLKit/HTMLElement.h @@ -24,7 +24,7 @@ - (instancetype)initWithTagName:(NSString *)tagName; - (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes; -- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes namespace:(HTMLNamespace)htmlNamespace; +- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes; - (BOOL)hasAttribute:(NSString *)name; - (NSString *)objectForKeyedSubscript:(NSString *)name; From de597a51a40810eea28971417dd2c6e3877336a8 Mon Sep 17 00:00:00 2001 From: iska Date: Wed, 27 May 2015 00:42:29 +0200 Subject: [PATCH 23/55] Reorganize HTML DOM classes --- HTMLKit.xcodeproj/project.pbxproj | 30 +++++++++---------- HTMLKit/HTMLDocument.m | 2 +- ...KitExceptions.h => HTMLKitDOMExceptions.h} | 0 ...KitExceptions.m => HTMLKitDOMExceptions.m} | 2 +- HTMLKit/HTMLNode.h | 2 ++ HTMLKit/HTMLNode.m | 15 +++++++++- 6 files changed, 33 insertions(+), 18 deletions(-) rename HTMLKit/{HTMLKitExceptions.h => HTMLKitDOMExceptions.h} (100%) rename HTMLKit/{HTMLKitExceptions.m => HTMLKitDOMExceptions.m} (91%) diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 8490df7..408e504 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -49,9 +49,9 @@ 6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857991A9E8934003A45D9 /* HTMLComment.h */; }; 6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238579A1A9E8934003A45D9 /* HTMLComment.m */; }; 6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238579A1A9E8934003A45D9 /* HTMLComment.m */; }; - 6238C9851AB8D6330006512E /* HTMLKitExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */; }; - 6238C9861AB8D6330006512E /* HTMLKitExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */; }; - 6238C9871AB8D6330006512E /* HTMLKitExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */; }; + 6238C9851AB8D6330006512E /* HTMLKitDOMExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */; }; + 6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */; }; + 6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */; }; 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */; }; 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; @@ -198,8 +198,8 @@ 623857951A9E8606003A45D9 /* HTMLDocumentType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDocumentType.m; sourceTree = ""; }; 623857991A9E8934003A45D9 /* HTMLComment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLComment.h; sourceTree = ""; }; 6238579A1A9E8934003A45D9 /* HTMLComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLComment.m; sourceTree = ""; }; - 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitExceptions.h; sourceTree = ""; }; - 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitExceptions.m; sourceTree = ""; }; + 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitDOMExceptions.h; sourceTree = ""; }; + 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitDOMExceptions.m; sourceTree = ""; }; 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTreeEnumerator.h; sourceTree = ""; }; 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTreeEnumerator.m; sourceTree = ""; }; 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeTreeEnumratorTests.m; sourceTree = ""; }; @@ -319,22 +319,25 @@ 62362A3F1A9FDE8A00301989 /* HTMLDOM.h */, 6234584C1A9D2FA4009BD491 /* HTMLNode.h */, 6234584D1A9D2FA4009BD491 /* HTMLNode.m */, - 6279F87219E1808D00F12EE5 /* HTMLElement.h */, - 6279F87319E1808D00F12EE5 /* HTMLElement.m */, 6238578F1A9E772B003A45D9 /* HTMLDocument.h */, 623857901A9E772B003A45D9 /* HTMLDocument.m */, 623857941A9E8606003A45D9 /* HTMLDocumentType.h */, 623857951A9E8606003A45D9 /* HTMLDocumentType.m */, + 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */, + 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */, + 6279F87219E1808D00F12EE5 /* HTMLElement.h */, + 6279F87319E1808D00F12EE5 /* HTMLElement.m */, 623857991A9E8934003A45D9 /* HTMLComment.h */, 6238579A1A9E8934003A45D9 /* HTMLComment.m */, 62362A3A1A9FA70400301989 /* HTMLText.h */, 62362A3B1A9FA70400301989 /* HTMLText.m */, 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */, 623406E01ADB04F9004677A3 /* HTMLTemplate.m */, - 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */, - 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */, 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */, 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */, + 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */, + 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */, + 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */, ); name = DOM; @@ -417,8 +420,6 @@ children = ( 625A14BC19C7829400AD0C32 /* HTMLKit.h */, 625A14BD19C7829400AD0C32 /* HTMLKit.m */, - 6238C9831AB8D6330006512E /* HTMLKitExceptions.h */, - 6238C9841AB8D6330006512E /* HTMLKitExceptions.m */, 62AE593219F97CCA0043F069 /* Tokenizing */, 628E16EC1ADAE71700B15A06 /* Parsing */, 623719441AA1472B002E03C8 /* DOM */, @@ -473,7 +474,6 @@ 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */, 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */, 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */, - 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */, 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */, 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */, @@ -556,7 +556,7 @@ 62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */, 623406E11ADB04F9004677A3 /* HTMLTemplate.h in Headers */, 6234BEEE1AABBF1400DEB15F /* HTMLStackOfOpenElements.h in Headers */, - 6238C9851AB8D6330006512E /* HTMLKitExceptions.h in Headers */, + 6238C9851AB8D6330006512E /* HTMLKitDOMExceptions.h in Headers */, 6238039F1AB63A8C008A53D0 /* HTMLEOFToken.h in Headers */, 6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */, 62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */, @@ -736,7 +736,7 @@ 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */, 62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, 623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */, - 6238C9871AB8D6330006512E /* HTMLKitExceptions.m in Sources */, + 6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */, 62F873ED19E088C90062683C /* HTMLParser.m in Sources */, 623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */, 6279F87619E1808D00F12EE5 /* HTMLElement.m in Sources */, @@ -788,7 +788,7 @@ 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */, 62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, 623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */, - 6238C9861AB8D6330006512E /* HTMLKitExceptions.m in Sources */, + 6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */, 62F873EC19E088C90062683C /* HTMLParser.m in Sources */, 623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */, 6279F87519E1808D00F12EE5 /* HTMLElement.m in Sources */, diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 7ae4e68..c910f50 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -8,7 +8,7 @@ #import "HTMLDocument.h" #import "HTMLParser.h" -#import "HTMLKitExceptions.h" +#import "HTMLKitDOMExceptions.h" @interface HTMLNode (Private) @property (nonatomic, weak) HTMLDocument *ownerDocument; diff --git a/HTMLKit/HTMLKitExceptions.h b/HTMLKit/HTMLKitDOMExceptions.h similarity index 100% rename from HTMLKit/HTMLKitExceptions.h rename to HTMLKit/HTMLKitDOMExceptions.h diff --git a/HTMLKit/HTMLKitExceptions.m b/HTMLKit/HTMLKitDOMExceptions.m similarity index 91% rename from HTMLKit/HTMLKitExceptions.m rename to HTMLKit/HTMLKitDOMExceptions.m index 8234e00..f7fcacc 100644 --- a/HTMLKit/HTMLKitExceptions.m +++ b/HTMLKit/HTMLKitDOMExceptions.m @@ -6,7 +6,7 @@ // Copyright (c) 2015 BrainCookie. All rights reserved. // -#import "HTMLKitExceptions.h" +#import "HTMLKitDOMExceptions.h" NSString * const HTMLKitHierarchyRequestError = @"HierarchyRequestError"; NSString * const HTMLKitNotFoundError = @"NotFoundError"; diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index 916ed3e..3ef90cf 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -97,6 +97,8 @@ typedef NS_ENUM(short, HTMLNodeType) - (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block; +- (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block; + - (NSEnumerator *)treeEnumerator; - (NSEnumerator *)reverseTreeEnumerator; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 363424d..0babcf9 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -12,7 +12,7 @@ #import "HTMLElement.h" #import "HTMLText.h" #import "HTMLComment.h" -#import "HTMLKitExceptions.h" +#import "HTMLKitDOMExceptions.h" #import "HTMLNodeTreeEnumerator.h" @interface HTMLNode () @@ -263,6 +263,19 @@ }]; } +- (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block +{ + if (block == nil) { + return; + } + + [self.childNodes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if ([obj isKindOfClass:[HTMLElement class]]) { + block([obj asElement], idx, stop); + } + }]; +} + - (NSEnumerator *)treeEnumerator { return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:NO]; From f8ee4a38b4cc7d8b5a96e5a8d38891f16fdb147f Mon Sep 17 00:00:00 2001 From: iska Date: Wed, 27 May 2015 00:59:10 +0200 Subject: [PATCH 24/55] Add HTML Node Filter protocol --- HTMLKit.xcodeproj/project.pbxproj | 4 ++++ HTMLKit/HTMLNodeFilter.h | 36 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 HTMLKit/HTMLNodeFilter.h diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 408e504..d906780 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -69,6 +69,7 @@ 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; }; 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; + 6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */; }; 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */; }; 624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; }; 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; }; @@ -212,6 +213,7 @@ 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerStates.h; sourceTree = ""; }; 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLToken.h; sourceTree = ""; }; 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLToken.m; sourceTree = ""; }; + 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; sourceTree = ""; }; 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokens.h; sourceTree = ""; }; 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTokenizerTests.m; sourceTree = ""; }; 624AC90419FBFE8A00BD3C4A /* html5lib-tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "html5lib-tests"; sourceTree = ""; }; @@ -333,6 +335,7 @@ 62362A3B1A9FA70400301989 /* HTMLText.m */, 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */, 623406E01ADB04F9004677A3 /* HTMLTemplate.m */, + 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */, 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */, 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */, 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */, @@ -563,6 +566,7 @@ 623857911A9E772B003A45D9 /* HTMLDocument.h in Headers */, 624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */, 62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */, + 6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */, 623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */, 625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */, 625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */, diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h new file mode 100644 index 0000000..2f302f2 --- /dev/null +++ b/HTMLKit/HTMLNodeFilter.h @@ -0,0 +1,36 @@ +// +// HTMLNodeFilter.h +// HTMLKit +// +// Created by Iska on 27/05/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import + +typedef NS_ENUM(unsigned short, HTMLNodeFilterValue) +{ + HTMLNodeFilterAccept = 1, + HTMLNodeFilterReject = 2, + HTMLNodeFilterSkip = 3 +} HTMLNodeFilterValue; + +typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) +{ + HTMLNodeFilterShowAll = 0xFFFFFFFF; + HTMLNodeFilterShowElement = 0x1; + HTMLNodeFilterShowText = 0x4; + HTMLNodeFilterShowProcessingInstruction = 0x40; + HTMLNodeFilterShowComment = 0x80; + HTMLNodeFilterShowDocument = 0x100; + HTMLNodeFilterShowDocumentType = 0x200; + HTMLNodeFilterShowDocumentFragment = 0x400; +} HTMLNodeFilterShowOptions; + +@class HTMLNode; + +@protocol HTMLNodeFilter + +- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node; + +@end From 7892a925c8f75fae735632867084e2ac3deef60f Mon Sep 17 00:00:00 2001 From: iska Date: Wed, 27 May 2015 00:59:37 +0200 Subject: [PATCH 25/55] Fix Element initializer after reordering the arguments --- HTMLKit/HTMLElement.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HTMLKit/HTMLElement.m b/HTMLKit/HTMLElement.m index 8e9a18f..47df5f9 100644 --- a/HTMLKit/HTMLElement.m +++ b/HTMLKit/HTMLElement.m @@ -36,10 +36,10 @@ - (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes { - return [self initWithTagName:tagName attributes:attributes namespace:HTMLNamespaceHTML]; + return [self initWithTagName:tagName namespace:HTMLNamespaceHTML attributes:attributes]; } -- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSDictionary *)attributes namespace:(HTMLNamespace)htmlNamespace +- (instancetype)initWithTagName:(NSString *)tagName namespace:(HTMLNamespace)htmlNamespace attributes:(NSDictionary *)attributes { self = [super initWithName:tagName type:HTMLNodeElement]; if (self) { From 45156d98fd9690ca339e81db6f9ca70c03b94a0f Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 00:34:17 +0200 Subject: [PATCH 26/55] Add implementation for HTML Node Iterator This, along with Tree Walker, will replace the Tree Enumerator. https://dom.spec.whatwg.org/#interface-nodeiterator --- HTMLKit.xcodeproj/project.pbxproj | 10 +++ HTMLKit/HTMLNodeFilter.h | 20 ++--- HTMLKit/HTMLNodeIterator.h | 27 ++++++ HTMLKit/HTMLNodeIterator.m | 131 ++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 HTMLKit/HTMLNodeIterator.h create mode 100644 HTMLKit/HTMLNodeIterator.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index d906780..88ae430 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -69,6 +69,9 @@ 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; }; 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; + 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; }; + 6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; }; + 6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; }; 6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */; }; 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */; }; 624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; }; @@ -213,6 +216,8 @@ 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerStates.h; sourceTree = ""; }; 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLToken.h; sourceTree = ""; }; 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLToken.m; sourceTree = ""; }; + 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = ""; }; + 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = ""; }; 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; sourceTree = ""; }; 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokens.h; sourceTree = ""; }; 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTokenizerTests.m; sourceTree = ""; }; @@ -335,6 +340,8 @@ 62362A3B1A9FA70400301989 /* HTMLText.m */, 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */, 623406E01ADB04F9004677A3 /* HTMLTemplate.m */, + 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */, + 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */, 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */, 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */, 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */, @@ -568,6 +575,7 @@ 62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */, 6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */, 623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */, + 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */, 625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */, 625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */, 6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */, @@ -729,6 +737,7 @@ files = ( 62362A3E1A9FA70400301989 /* HTMLText.m in Sources */, 6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */, + 6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */, 6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */, 6234BEF01AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */, 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, @@ -781,6 +790,7 @@ files = ( 62362A3D1A9FA70400301989 /* HTMLText.m in Sources */, 6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */, + 6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */, 6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */, 6234BEEF1AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */, 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h index 2f302f2..31cbdfa 100644 --- a/HTMLKit/HTMLNodeFilter.h +++ b/HTMLKit/HTMLNodeFilter.h @@ -13,19 +13,19 @@ typedef NS_ENUM(unsigned short, HTMLNodeFilterValue) HTMLNodeFilterAccept = 1, HTMLNodeFilterReject = 2, HTMLNodeFilterSkip = 3 -} HTMLNodeFilterValue; +}; typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) { - HTMLNodeFilterShowAll = 0xFFFFFFFF; - HTMLNodeFilterShowElement = 0x1; - HTMLNodeFilterShowText = 0x4; - HTMLNodeFilterShowProcessingInstruction = 0x40; - HTMLNodeFilterShowComment = 0x80; - HTMLNodeFilterShowDocument = 0x100; - HTMLNodeFilterShowDocumentType = 0x200; - HTMLNodeFilterShowDocumentFragment = 0x400; -} HTMLNodeFilterShowOptions; + HTMLNodeFilterShowAll = 0xFFFFFFFF, + HTMLNodeFilterShowElement = 0x1, + HTMLNodeFilterShowText = 0x4, + HTMLNodeFilterShowProcessingInstruction = 0x40, + HTMLNodeFilterShowComment = 0x80, + HTMLNodeFilterShowDocument = 0x100, + HTMLNodeFilterShowDocumentType = 0x200, + HTMLNodeFilterShowDocumentFragment = 0x400 +}; @class HTMLNode; diff --git a/HTMLKit/HTMLNodeIterator.h b/HTMLKit/HTMLNodeIterator.h new file mode 100644 index 0000000..f082809 --- /dev/null +++ b/HTMLKit/HTMLNodeIterator.h @@ -0,0 +1,27 @@ +// +// HTMLNodeIterator.h +// HTMLKit +// +// Created by Iska on 27/05/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import +#import "HTMLNodeFilter.h" + +@class HTMLNode; + +@interface HTMLNodeIterator : NSEnumerator + +@property (nonatomic, strong, readonly) HTMLNode *root; +@property (nonatomic, strong, readonly) HTMLNode *referenceNode; +@property (nonatomic, assign, readonly) BOOL pointerBeforeReferenceNode; +@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow; +@property (nonatomic, strong, readonly) id filter; + +- (instancetype)initWithNode:(HTMLNode *)node; + +- (HTMLNode *)nextNode; +- (HTMLNode *)previousNode; + +@end diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m new file mode 100644 index 0000000..1439bdb --- /dev/null +++ b/HTMLKit/HTMLNodeIterator.m @@ -0,0 +1,131 @@ +// +// HTMLNodeIterator.m +// HTMLKit +// +// Created by Iska on 27/05/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNodeIterator.h" +#import "HTMLNode.h" + +typedef NS_ENUM(short, TraverseDirection) +{ + TraverseDirectionNext, + TraverseDirectionPrevious +}; + +@interface HTMLNodeIterator () +{ + HTMLNode *_root; +} +@end + +@implementation HTMLNodeIterator + +- (instancetype)initWithNode:(HTMLNode *)node +{ + self = [super init]; + if (self) { + _root = node; + _whatToShow = HTMLNodeFilterShowAll; + } + return self; +} + +- (HTMLNode *)traverseInDirection:(TraverseDirection)direction +{ + HTMLNode *node = self.referenceNode; + BOOL beforeNode = self.pointerBeforeReferenceNode; + + do { + if (direction == TraverseDirectionNext) { + if (!beforeNode) { + node = FollowingNode(node, self.root); + if (node == nil) { + return nil; + } + beforeNode = NO; + } + } else { + if (beforeNode) { + node = PrecedingNode(node, self.root); + if (node == nil) { + return nil; + } + beforeNode = YES; + } + } + } while (FilterNode(self, node) != HTMLNodeFilterAccept); + + _referenceNode = node; + _pointerBeforeReferenceNode = beforeNode; + return node; +} + +NS_INLINE HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root) +{ + HTMLNode *previous = node.previousSibling; + if (previous != nil) { + while (node.lastChildNode != nil) { + node = node.lastChildNode; + } + return node; + } + + if (node == root) { + return nil; + } + + return node.parentNode; +} + +NS_INLINE HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root) +{ + if (node.firstChiledNode != nil) { + return node.firstChiledNode; + } + + do { + if (node == root) { + return nil; + } + if (node.nextSibling != nil) { + return node.nextSibling; + } + node = node.parentNode; + } while (node != nil); + + return nil; +} + +NS_INLINE HTMLNodeFilterValue FilterNode(HTMLNodeIterator *iterator, HTMLNode *node) +{ + unsigned long nthBit = (1 << (node.nodeType - 1)) & iterator.whatToShow; + if (!nthBit) { + return HTMLNodeFilterSkip; + } + + if (iterator.filter == nil) { + return HTMLNodeFilterAccept; + } + + return [iterator.filter acceptNode:node]; +} + +- (id)nextObject +{ + return self.nextNode; +} + +- (HTMLNode *)nextNode +{ + return [self traverseInDirection:TraverseDirectionNext]; +} + +- (HTMLNode *)previousNode +{ + return [self traverseInDirection:TraverseDirectionPrevious]; +} + +@end From aece7138a41839278d57b6db5366b0b64e90b60a Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 00:42:20 +0200 Subject: [PATCH 27/55] Rename Tree Enumeration Tests to Node Iterator Tests --- HTMLKit.xcodeproj/project.pbxproj | 12 +++--- ...atorTests.m => HTMLKitNodeIteratorTests.m} | 39 +++++++++---------- 2 files changed, 25 insertions(+), 26 deletions(-) rename HTMLKitTests/{HTMLKitNodeTreeEnumratorTests.m => HTMLKitNodeIteratorTests.m} (68%) diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 88ae430..f01e630 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -55,8 +55,8 @@ 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */; }; 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; - 623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */; }; - 623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */; }; + 623916C71AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; }; + 623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; }; 6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; }; 6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; }; 6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; }; @@ -206,7 +206,7 @@ 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitDOMExceptions.m; sourceTree = ""; }; 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTreeEnumerator.h; sourceTree = ""; }; 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTreeEnumerator.m; sourceTree = ""; }; - 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeTreeEnumratorTests.m; sourceTree = ""; }; + 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeIteratorTests.m; sourceTree = ""; }; 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTreeConstructionTests.m; sourceTree = ""; }; 6239755C1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTML5LibTreeConstructionTest.h; sourceTree = ""; }; 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTML5LibTreeConstructionTest.m; sourceTree = ""; }; @@ -367,7 +367,7 @@ 624B9FB71AE072CB00646C4C /* Nodes */ = { isa = PBXGroup; children = ( - 623916C61AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m */, + 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */, 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */, 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */, @@ -777,7 +777,7 @@ 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, 624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, 624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */, - 623916C81AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, + 623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */, 62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, 6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, 624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */, @@ -830,7 +830,7 @@ 6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, 624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */, 624B9FB21AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */, - 623916C71AC7209E0066B4FE /* HTMLKitNodeTreeEnumratorTests.m in Sources */, + 623916C71AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */, 62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */, 6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */, 624FC37A1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */, diff --git a/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m similarity index 68% rename from HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m rename to HTMLKitTests/HTMLKitNodeIteratorTests.m index 654fbcf..27a699a 100644 --- a/HTMLKitTests/HTMLKitNodeTreeEnumratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -7,14 +7,13 @@ // #import +#import "HTMLDOM.h" -#import "HTMLNodes.h" - -@interface HTMLKitNodeTreeEnumratorTests : XCTestCase +@interface HTMLKitNodeIteratorTests : XCTestCase @end -@implementation HTMLKitNodeTreeEnumratorTests +@implementation HTMLKitNodeIteratorTests #pragma mark - Elements @@ -92,7 +91,7 @@ - (void)testSingle { HTMLElement *div = self.div; - NSArray *result = div.treeEnumerator.allObjects; + NSArray *result = div.nodeIterator.allObjects; NSArray *expected = @[@"div"]; XCTAssertEqualObjects([result valueForKey:@"name"], expected); } @@ -100,49 +99,49 @@ - (void)testSimpleTree { HTMLElement *tree = self.simpleTree; - NSArray *result = tree.treeEnumerator.allObjects; + NSArray *result = tree.nodeIterator.allObjects; NSArray *expected = @[@"div", @"a", @"b", @"c"]; XCTAssertEqualObjects([result valueForKey:@"name"], expected); } - (void)testSimpleTreeReversed { - HTMLElement *tree = self.simpleTree; - NSArray *result = tree.reverseTreeEnumerator.allObjects; - NSArray *expected = @[@"div", @"c", @"b", @"a"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); +// HTMLElement *tree = self.simpleTree; +// NSArray *result = tree.reverseTreeEnumerator.allObjects; +// NSArray *expected = @[@"div", @"c", @"b", @"a"]; +// XCTAssertEqualObjects([result valueForKey:@"name"], expected); } - (void)testNestedSimpleTree { HTMLElement *tree = self.nestedSimpleTree; - NSArray *result = tree.treeEnumerator.allObjects; + NSArray *result = tree.nodeIterator.allObjects; NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"]; XCTAssertEqualObjects([result valueForKey:@"name"], expected); } - (void)testNestedSimpleTreeReversed { - HTMLElement *tree = self.nestedSimpleTree; - NSArray *result = tree.reverseTreeEnumerator.allObjects; - NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"div", @"c", @"b", @"a"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); +// HTMLElement *tree = self.nestedSimpleTree; +// NSArray *result = tree.reverseTreeEnumerator.allObjects; +// NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"div", @"c", @"b", @"a"]; +// XCTAssertEqualObjects([result valueForKey:@"name"], expected); } - (void)testComplexSimpleTree { HTMLElement *tree = self.complexTree; - NSArray *result = tree.treeEnumerator.allObjects; + NSArray *result = tree.nodeIterator.allObjects; NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"]; XCTAssertEqualObjects([result valueForKey:@"name"], expected); } - (void)testComplexSimpleTreeReversed { - HTMLElement *tree = self.complexTree; - NSArray *result = tree.reverseTreeEnumerator.allObjects; - NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"e", @"f", @"div", @"div", @"c", @"b", @"a"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); +// HTMLElement *tree = self.complexTree; +// NSArray *result = tree.reverseTreeEnumerator.allObjects; +// NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"e", @"f", @"div", @"div", @"c", @"b", @"a"]; +// XCTAssertEqualObjects([result valueForKey:@"name"], expected); } @end From 165cdb5a75543c1f0de95f2dc05fdb3b04b0525c Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 00:42:59 +0200 Subject: [PATCH 28/55] Fix HTML Element initializer usage after arguments reordering --- HTMLKit/HTMLParser.m | 4 ++-- HTMLKitTests/HTML5LibTreeConstructionTest.m | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m index 539ce36..d561e59 100644 --- a/HTMLKit/HTMLParser.m +++ b/HTMLKit/HTMLParser.m @@ -421,8 +421,8 @@ - (HTMLElement *)createElementForToken:(HTMLTagToken *)token inNamespace:(HTMLNamespace)htmlNamespace { HTMLElement *element = [[HTMLElement alloc] initWithTagName:token.tagName - attributes:token.attributes - namespace:htmlNamespace]; + namespace:htmlNamespace + attributes:token.attributes]; return element; } diff --git a/HTMLKitTests/HTML5LibTreeConstructionTest.m b/HTMLKitTests/HTML5LibTreeConstructionTest.m index 1e8ac4d..37bea03 100644 --- a/HTMLKitTests/HTML5LibTreeConstructionTest.m +++ b/HTMLKitTests/HTML5LibTreeConstructionTest.m @@ -111,7 +111,7 @@ static NSString * const TreeConstruction = @"tree-construction"; fragment = [fragment substringFromIndex:@"svg ".length]; namespace = HTMLNamespaceSVG; } - test.documentFragment = [[HTMLElement alloc] initWithTagName:fragment attributes:nil namespace:namespace]; + test.documentFragment = [[HTMLElement alloc] initWithTagName:fragment namespace:namespace attributes:nil]; } else if ([match hasPrefix:@"#document\n"]) { NSArray *parts = [[match substringFromIndex:@"#document\n".length] componentsSeparatedByString:@"| "]; NSArray *nodes = [HTML5LibTreeConstructionTest parseDocument:parts]; @@ -245,7 +245,7 @@ NS_INLINE HTMLElement * parseTag(NSString *str) NSString *tagName = parts.count == 2 ? parts[1] : parts[0]; HTMLNamespace namespace = parts.count == 1 ? HTMLNamespaceHTML : ([parts[0] isEqualToString:@"math"] ? HTMLNamespaceMathML : HTMLNamespaceSVG); - HTMLElement *element = [[HTMLElement alloc] initWithTagName:tagName attributes:nil namespace:namespace]; + HTMLElement *element = [[HTMLElement alloc] initWithTagName:tagName namespace:namespace attributes:nil]; return element; } From ac561570f3ee288ca6ecd6f706fd580f5d14f3c9 Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 00:43:27 +0200 Subject: [PATCH 29/55] Use correct DOM import in test classes --- HTMLKitTests/HTMLKitMutationAlgorithmsTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m index 990d574..421065b 100644 --- a/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m +++ b/HTMLKitTests/HTMLKitMutationAlgorithmsTests.m @@ -7,7 +7,7 @@ // #import -#import "HTMLNodes.h" +#import "HTMLDOM.h" #import "NSString+HTMLKit.h" extern uint64_t dispatch_benchmark(size_t count, void (^block)(void)); From 13ed63017eb1216314744f95a2e601e318a74a83 Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 00:55:45 +0200 Subject: [PATCH 30/55] Fix initial Node Iterator values --- HTMLKit/HTMLNodeIterator.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index 1439bdb..0c14127 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -28,6 +28,8 @@ typedef NS_ENUM(short, TraverseDirection) self = [super init]; if (self) { _root = node; + _referenceNode = _root; + _pointerBeforeReferenceNode = YES; _whatToShow = HTMLNodeFilterShowAll; } return self; From 0fa394b911124658e0ed11e2de62ca49fb42597f Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 00:56:09 +0200 Subject: [PATCH 31/55] Fix traversing method in Node Iterator --- HTMLKit/HTMLNodeIterator.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index 0c14127..8da7de0 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -47,16 +47,16 @@ typedef NS_ENUM(short, TraverseDirection) if (node == nil) { return nil; } - beforeNode = NO; } + beforeNode = NO; } else { if (beforeNode) { node = PrecedingNode(node, self.root); if (node == nil) { return nil; } - beforeNode = YES; } + beforeNode = YES; } } while (FilterNode(self, node) != HTMLNodeFilterAccept); @@ -69,10 +69,10 @@ NS_INLINE HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root) { HTMLNode *previous = node.previousSibling; if (previous != nil) { - while (node.lastChildNode != nil) { - node = node.lastChildNode; + while (previous.lastChildNode != nil) { + previous = previous.lastChildNode; } - return node; + return previous; } if (node == root) { From cd6e8bf4fc7e78ec338e81b81eebdd2b617f1427 Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 01:22:29 +0200 Subject: [PATCH 32/55] Remove Tree Enumerator since it was superseded by the Node Iterator implementation --- HTMLKit.xcodeproj/project.pbxproj | 10 ----- HTMLKit/HTMLElement.m | 2 +- HTMLKit/HTMLNode.h | 5 +-- HTMLKit/HTMLNode.m | 22 +--------- HTMLKit/HTMLNodeTreeEnumerator.h | 17 -------- HTMLKit/HTMLNodeTreeEnumerator.m | 54 ------------------------- HTMLKitTests/HTMLKitNodeIteratorTests.m | 38 ++++++++--------- 7 files changed, 22 insertions(+), 126 deletions(-) delete mode 100644 HTMLKit/HTMLNodeTreeEnumerator.h delete mode 100644 HTMLKit/HTMLNodeTreeEnumerator.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index f01e630..06880b2 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -52,9 +52,6 @@ 6238C9851AB8D6330006512E /* HTMLKitDOMExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */; }; 6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */; }; 6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */; }; - 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */; }; - 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; - 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */; }; 623916C71AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; }; 623916C81AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; }; 6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */; }; @@ -204,8 +201,6 @@ 6238579A1A9E8934003A45D9 /* HTMLComment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLComment.m; sourceTree = ""; }; 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitDOMExceptions.h; sourceTree = ""; }; 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitDOMExceptions.m; sourceTree = ""; }; - 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTreeEnumerator.h; sourceTree = ""; }; - 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTreeEnumerator.m; sourceTree = ""; }; 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitNodeIteratorTests.m; sourceTree = ""; }; 623975591AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTreeConstructionTests.m; sourceTree = ""; }; 6239755C1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTML5LibTreeConstructionTest.h; sourceTree = ""; }; @@ -343,8 +338,6 @@ 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */, 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */, 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */, - 623916C11AC707250066B4FE /* HTMLNodeTreeEnumerator.h */, - 623916C21AC707250066B4FE /* HTMLNodeTreeEnumerator.m */, 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */, 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */, 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, @@ -559,7 +552,6 @@ 62D89DB01AE7080300B6243D /* HTMLDOM.h in Headers */, 62F873EB19E088C90062683C /* HTMLParser.h in Headers */, 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */, - 623916C31AC707250066B4FE /* HTMLNodeTreeEnumerator.h in Headers */, 62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */, 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */, 6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */, @@ -746,7 +738,6 @@ 62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */, 62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */, - 623916C51AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */, 62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, 623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */, 6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */, @@ -799,7 +790,6 @@ 62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */, 62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */, - 623916C41AC707250066B4FE /* HTMLNodeTreeEnumerator.m in Sources */, 62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, 623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */, 6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */, diff --git a/HTMLKit/HTMLElement.m b/HTMLKit/HTMLElement.m index 47df5f9..1c018da 100644 --- a/HTMLKit/HTMLElement.m +++ b/HTMLKit/HTMLElement.m @@ -88,7 +88,7 @@ - (NSString *)textContent { NSMutableString *content = [NSMutableString string]; - for (HTMLNode *node in self.treeEnumerator) { + for (HTMLNode *node in self.nodeIterator) { if (node.nodeType == HTMLNodeText) { [content appendString:[(HTMLText *)node data]]; } diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index 3ef90cf..a2820f4 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -7,6 +7,7 @@ // #import +#import "HTMLNodeIterator.h" typedef NS_ENUM(short, HTMLNodeType) { @@ -99,9 +100,7 @@ typedef NS_ENUM(short, HTMLNodeType) - (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block; -- (NSEnumerator *)treeEnumerator; - -- (NSEnumerator *)reverseTreeEnumerator; +- (HTMLNodeIterator *)nodeIterator; - (NSString *)treeDescription; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 0babcf9..bb8bbf7 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -13,7 +13,6 @@ #import "HTMLText.h" #import "HTMLComment.h" #import "HTMLKitDOMExceptions.h" -#import "HTMLNodeTreeEnumerator.h" @interface HTMLNode () { @@ -276,28 +275,11 @@ }]; } -- (NSEnumerator *)treeEnumerator +- (HTMLNodeIterator *)nodeIterator { - return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:NO]; + return [[HTMLNodeIterator alloc] initWithNode:self]; } -- (NSEnumerator *)reverseTreeEnumerator -{ - return [[HTMLNodeTreeEnumerator alloc] initWithNode:self reverse:YES]; -} - -- (void)doInsertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child -{ - NSArray *nodes = node.nodeType == HTMLNodeDocumentFragment ? node.childNodes.array : @[node]; - - NSUInteger index = [self indexOfChildNode:child]; - if (index != NSNotFound) { - NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index, nodes.count)]; - [(NSMutableOrderedSet *)self.childNodes insertObjects:nodes atIndexes:indexes]; - } else { - [(NSMutableOrderedSet *)self.childNodes addObjectsFromArray:nodes]; - } -} #ifndef HTMLKIT_NO_DOM_CHECKS diff --git a/HTMLKit/HTMLNodeTreeEnumerator.h b/HTMLKit/HTMLNodeTreeEnumerator.h deleted file mode 100644 index baf7243..0000000 --- a/HTMLKit/HTMLNodeTreeEnumerator.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// HTMLNodeTreeEnumerator.h -// HTMLKit -// -// Created by Iska on 28/03/15. -// Copyright (c) 2015 BrainCookie. All rights reserved. -// - -#import - -@class HTMLNode; - -@interface HTMLNodeTreeEnumerator : NSEnumerator - -- (instancetype)initWithNode:(HTMLNode *)node reverse:(BOOL)reverse; - -@end diff --git a/HTMLKit/HTMLNodeTreeEnumerator.m b/HTMLKit/HTMLNodeTreeEnumerator.m deleted file mode 100644 index 8f82972..0000000 --- a/HTMLKit/HTMLNodeTreeEnumerator.m +++ /dev/null @@ -1,54 +0,0 @@ -// -// HTMLNodeTreeEnumerator.m -// HTMLKit -// -// Created by Iska on 28/03/15. -// Copyright (c) 2015 BrainCookie. All rights reserved. -// - -#import "HTMLNodeTreeEnumerator.h" -#import "HTMLNode.h" - -@interface HTMLNodeTreeEnumerator () -{ - BOOL _reverse; - NSMutableArray *_stack; -} -@end - -@implementation HTMLNodeTreeEnumerator - -- (instancetype)initWithNode:(HTMLNode *)node reverse:(BOOL)reverse -{ - self = [super init]; - if (self) { - _reverse = reverse; - _stack = [[NSMutableArray alloc] initWithObjects:node, nil]; - } - return self; -} - -- (id)nextObject -{ - if (_stack.count == 0) { - return nil; - } - - HTMLNode *node = _stack.lastObject; - [_stack removeLastObject]; - - NSArray *childNodes = node.childNodes.array; - if (childNodes != nil && childNodes.count > 0) { - if (childNodes.count > 1) { - NSRange range = NSMakeRange(_reverse ? 0 : 1, childNodes.count - 1); - NSArray *rest = [childNodes subarrayWithRange:range]; - - [_stack addObjectsFromArray:_reverse ? rest : rest.reverseObjectEnumerator.allObjects]; - } - [_stack addObject:_reverse ? childNodes.lastObject : childNodes.firstObject]; - } - - return node; -} - -@end diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m index 27a699a..ecea4bf 100644 --- a/HTMLKitTests/HTMLKitNodeIteratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -104,14 +104,6 @@ XCTAssertEqualObjects([result valueForKey:@"name"], expected); } -- (void)testSimpleTreeReversed -{ -// HTMLElement *tree = self.simpleTree; -// NSArray *result = tree.reverseTreeEnumerator.allObjects; -// NSArray *expected = @[@"div", @"c", @"b", @"a"]; -// XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - - (void)testNestedSimpleTree { HTMLElement *tree = self.nestedSimpleTree; @@ -120,14 +112,6 @@ XCTAssertEqualObjects([result valueForKey:@"name"], expected); } -- (void)testNestedSimpleTreeReversed -{ -// HTMLElement *tree = self.nestedSimpleTree; -// NSArray *result = tree.reverseTreeEnumerator.allObjects; -// NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"div", @"c", @"b", @"a"]; -// XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - - (void)testComplexSimpleTree { HTMLElement *tree = self.complexTree; @@ -136,12 +120,24 @@ XCTAssertEqualObjects([result valueForKey:@"name"], expected); } -- (void)testComplexSimpleTreeReversed +- (void)testNextPreviousIteration { -// HTMLElement *tree = self.complexTree; -// NSArray *result = tree.reverseTreeEnumerator.allObjects; -// NSArray *expected = @[@"div", @"div", @"c", @"b", @"a", @"e", @"f", @"div", @"div", @"c", @"b", @"a"]; -// XCTAssertEqualObjects([result valueForKey:@"name"], expected); + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.previousNode, nil); + XCTAssertEqualObjects(iterator.nextNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"div"); + XCTAssertEqualObjects(iterator.nextNode.name, @"a"); + XCTAssertEqualObjects(iterator.nextNode.name, @"b"); + XCTAssertEqualObjects(iterator.nextNode.name, @"c"); + XCTAssertEqualObjects(iterator.previousNode.name, @"c"); + XCTAssertEqualObjects(iterator.previousNode.name, @"b"); + XCTAssertEqualObjects(iterator.previousNode.name, @"a"); + XCTAssertEqualObjects(iterator.previousNode.name, @"div"); } @end From dfeebc7f7f685cef58db2bc93bed2fe1887b916d Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 22:56:49 +0200 Subject: [PATCH 33/55] Remove frameworks search path entry causing an ignored warning on build --- HTMLKit.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 06880b2..1c13dba 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -970,7 +970,6 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", - "$(SDKROOT)/Developer/Library/Frameworks", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; @@ -993,7 +992,6 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", - "$(SDKROOT)/Developer/Library/Frameworks", ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch"; @@ -1042,7 +1040,6 @@ buildSettings = { BUNDLE_LOADER = ""; FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); @@ -1066,7 +1063,6 @@ buildSettings = { BUNDLE_LOADER = ""; FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", "$(DEVELOPER_FRAMEWORKS_DIR)", ); From e1c3533a1c4ff3044b2314207885513c5f4f42f4 Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 22:57:31 +0200 Subject: [PATCH 34/55] Remove unused import in Tree Construction test class --- HTMLKitTests/HTML5LibTreeConstructionTest.m | 1 - 1 file changed, 1 deletion(-) diff --git a/HTMLKitTests/HTML5LibTreeConstructionTest.m b/HTMLKitTests/HTML5LibTreeConstructionTest.m index 37bea03..9d0a45c 100644 --- a/HTMLKitTests/HTML5LibTreeConstructionTest.m +++ b/HTMLKitTests/HTML5LibTreeConstructionTest.m @@ -13,7 +13,6 @@ #import "HTMLElement.h" #import "HTMLText.h" #import "HTMLComment.h" -#import "HTMLNodeTreeEnumerator.h" static NSString * const HTML5LibTests = @"html5lib-tests"; static NSString * const TreeConstruction = @"tree-construction"; From 3df5a219e162e2338189b05d85ed2dbbd9f7519b Mon Sep 17 00:00:00 2001 From: iska Date: Thu, 28 May 2015 23:26:08 +0200 Subject: [PATCH 35/55] Add Node Iterator initializers with filter and show-options arguments --- HTMLKit/HTMLNode.h | 3 ++- HTMLKit/HTMLNode.m | 7 ++++++- HTMLKit/HTMLNodeFilter.h | 1 - HTMLKit/HTMLNodeIterator.h | 6 ++++++ HTMLKit/HTMLNodeIterator.m | 16 +++++++++++++++- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index a2820f4..d9078f2 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -101,7 +101,8 @@ typedef NS_ENUM(short, HTMLNodeType) - (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block; - (HTMLNodeIterator *)nodeIterator; - +- (HTMLNodeIterator *)nodeIteratorWithFilter:(id)filter + showOptions:(HTMLNodeFilterShowOptions)showOptions; - (NSString *)treeDescription; @end diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index bb8bbf7..f65cf9b 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -277,9 +277,14 @@ - (HTMLNodeIterator *)nodeIterator { - return [[HTMLNodeIterator alloc] initWithNode:self]; + return [self nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowAll]; } +- (HTMLNodeIterator *)nodeIteratorWithFilter:(id)filter + showOptions:(HTMLNodeFilterShowOptions)showOptions +{ + return [[HTMLNodeIterator alloc] initWithNode:self filter:filter showOptions:showOptions]; +} #ifndef HTMLKIT_NO_DOM_CHECKS diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h index 31cbdfa..021fe5a 100644 --- a/HTMLKit/HTMLNodeFilter.h +++ b/HTMLKit/HTMLNodeFilter.h @@ -20,7 +20,6 @@ typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) HTMLNodeFilterShowAll = 0xFFFFFFFF, HTMLNodeFilterShowElement = 0x1, HTMLNodeFilterShowText = 0x4, - HTMLNodeFilterShowProcessingInstruction = 0x40, HTMLNodeFilterShowComment = 0x80, HTMLNodeFilterShowDocument = 0x100, HTMLNodeFilterShowDocumentType = 0x200, diff --git a/HTMLKit/HTMLNodeIterator.h b/HTMLKit/HTMLNodeIterator.h index f082809..d6c0cb7 100644 --- a/HTMLKit/HTMLNodeIterator.h +++ b/HTMLKit/HTMLNodeIterator.h @@ -20,6 +20,12 @@ @property (nonatomic, strong, readonly) id filter; - (instancetype)initWithNode:(HTMLNode *)node; +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id)filter; +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id)filter + showOptions:(HTMLNodeFilterShowOptions)showOptions; + - (HTMLNode *)nextNode; - (HTMLNode *)previousNode; diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index 8da7de0..370155e 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -24,13 +24,27 @@ typedef NS_ENUM(short, TraverseDirection) @implementation HTMLNodeIterator - (instancetype)initWithNode:(HTMLNode *)node +{ + return [self initWithNode:node filter:nil]; +} + +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id)filter +{ + return [self initWithNode:node filter:nil showOptions:HTMLNodeFilterShowAll]; +} + +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id)filter + showOptions:(HTMLNodeFilterShowOptions)showOptions { self = [super init]; if (self) { _root = node; + _filter = filter; + _whatToShow = showOptions; _referenceNode = _root; _pointerBeforeReferenceNode = YES; - _whatToShow = HTMLNodeFilterShowAll; } return self; } From 066bdeffa82ed57fd6c01ed87589a47b41ececa5 Mon Sep 17 00:00:00 2001 From: iska Date: Fri, 29 May 2015 00:39:52 +0200 Subject: [PATCH 36/55] Add Node Iterator test cases for iteration and filtering logic --- HTMLKitTests/HTMLKitNodeIteratorTests.m | 219 +++++++++++++++++++++--- 1 file changed, 197 insertions(+), 22 deletions(-) diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m index ecea4bf..9eb9c7f 100644 --- a/HTMLKitTests/HTMLKitNodeIteratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -9,6 +9,23 @@ #import #import "HTMLDOM.h" +@interface CommentNodeFilter : NSObject +@end + +@implementation CommentNodeFilter + +- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node +{ + if (node.nodeType == HTMLNodeComment) { + if ([[(HTMLComment *)node data] rangeOfString:@"second"].location != NSNotFound) { + return HTMLNodeFilterAccept; + } + } + return HTMLNodeFilterReject; +} + +@end + @interface HTMLKitNodeIteratorTests : XCTestCase @end @@ -86,38 +103,77 @@ return root; } +- (HTMLDocument *)mixedTree +{ + + /* + | doctype + | #comment + | #comment + | html + | #text "This is a Text" + | div + */ + HTMLDocument *document = [HTMLDocument new]; + document.documentType = [HTMLDocumentType new];; + + HTMLComment *comment = [[HTMLComment alloc] initWithData:@"This is a Comment"]; + [document appendNode:comment]; + + HTMLComment *secondCommnet = [[HTMLComment alloc] initWithData:@"This is a second Comment"]; + [document appendNode:secondCommnet]; + + HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"html"]; + [document appendNode:root]; + + [root appendNode:[[HTMLText alloc] initWithData:@"This is a Text"]]; + + [root appendNode:[[HTMLElement alloc] initWithTagName:@"div"]]; + + return document; +} + #pragma mark - Tests -- (void)testSingle -{ - HTMLElement *div = self.div; - NSArray *result = div.nodeIterator.allObjects; - NSArray *expected = @[@"div"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); -} - -- (void)testSimpleTree +- (void)testNodeIteratorInit { HTMLElement *tree = self.simpleTree; - NSArray *result = tree.nodeIterator.allObjects; - NSArray *expected = @[@"div", @"a", @"b", @"c"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.root, tree); + XCTAssertEqualObjects(iterator.referenceNode, tree); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + XCTAssertEqualObjects(iterator.filter, nil); + XCTAssertEqual(iterator.whatToShow, HTMLNodeFilterShowAll); } -- (void)testNestedSimpleTree +- (void)testNewIteratorNextNodeShouldBeRoot { - HTMLElement *tree = self.nestedSimpleTree; - NSArray *result = tree.nodeIterator.allObjects; - NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.nextNode, tree); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); } -- (void)testComplexSimpleTree +- (void)testNewIteratorPreviousNodeShouldBeNil { - HTMLElement *tree = self.complexTree; - NSArray *result = tree.nodeIterator.allObjects; - NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"]; - XCTAssertEqualObjects([result valueForKey:@"name"], expected); + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + XCTAssertEqualObjects(iterator.previousNode, nil); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testNewIteratorPreviousNodeShouldBeNextNode +{ + HTMLElement *tree = self.simpleTree; + HTMLNodeIterator *iterator = tree.nodeIterator; + + HTMLNode *node = iterator.nextNode; + + XCTAssertEqualObjects(iterator.previousNode, node); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); } - (void)testNextPreviousIteration @@ -140,4 +196,123 @@ XCTAssertEqualObjects(iterator.previousNode.name, @"div"); } +- (void)testSingleNodeIteration +{ + HTMLElement *div = self.div; + NSArray *result = div.nodeIterator.allObjects; + NSArray *expected = @[@"div"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testSimpleTreeIteration +{ + HTMLElement *tree = self.simpleTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testNestedSimpleTreeIteration +{ + HTMLElement *tree = self.nestedSimpleTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"div", @"a", @"b", @"c", @"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testComplexTreeIteration +{ + HTMLElement *tree = self.complexTree; + NSArray *result = tree.nodeIterator.allObjects; + NSArray *expected = @[@"div", @"div",@"div", @"a", @"b", @"c", @"e", @"f", @"div", @"a", @"b", @"c"]; + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowDocument +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowDocument]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#document"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLDocument class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowDocumentType +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowDocumentType]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLDocumentType class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowComment +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowComment]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#comment", @"#comment"]; + XCTAssertEqual(result.count, 2); + XCTAssertEqual([result[0] class], [HTMLComment class]); + XCTAssertEqual([result[1] class], [HTMLComment class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowText +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowText]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#text"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqual([result.firstObject class], [HTMLText class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowElement +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowElement]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html", @"div"]; + XCTAssertEqual(result.count, 2); + XCTAssertEqual([result.firstObject class], [HTMLElement class]); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testShowBitmask +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil + showOptions:HTMLNodeFilterShowElement | HTMLNodeFilterShowText]; + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"html", @"#text", @"div"]; + XCTAssertEqual(result.count, 3); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + +- (void)testNodeFilter +{ + HTMLDocument *document = self.mixedTree; + + HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:[CommentNodeFilter new] + showOptions:HTMLNodeFilterShowAll]; + + NSArray *result = iterator.allObjects; + NSArray *expected = @[@"#comment"]; + XCTAssertEqual(result.count, 1); + XCTAssertEqualObjects([result[0] data], @"This is a second Comment"); + XCTAssertEqualObjects([result valueForKey:@"name"], expected); +} + @end From 71fefa2fa95561dd4d62bcf96746290084a8a6b2 Mon Sep 17 00:00:00 2001 From: iska Date: Fri, 29 May 2015 20:16:17 +0200 Subject: [PATCH 37/55] Add methods to attach Node Iterators to the owner document node of the iterator's root This is a prerequisite to implement the iterator's removing steps. https://dom.spec.whatwg.org/#interface-nodeiterator --- HTMLKit/HTMLDocument.m | 14 ++++++++++++++ HTMLKit/HTMLNodeIterator.m | 28 +++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index c910f50..1837124 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -18,6 +18,7 @@ @interface HTMLDocument () { HTMLDocument *_inertTemplateDocument; + NSMutableArray *_nodeIterators; } @property (nonatomic, assign) HTMLDocumentReadyState readyState; @end @@ -37,6 +38,7 @@ self = [super initWithName:@"#document" type:HTMLNodeDocument]; if (self) { _readyState = HTMLDocumentLoading; + _nodeIterators = [NSMutableArray new]; } return self; } @@ -64,6 +66,18 @@ } } +#pragma mark - Node Iterators + +- (void)attachNodeIterator:(HTMLNodeIterator *)iterator +{ + [_nodeIterators addObject:iterator]; +} + +- (void)detachNodeIterator:(HTMLNodeIterator *)iterator +{ + [_nodeIterators removeObject:iterator]; +} + #pragma mark - Mutation Algorithms - (HTMLNode *)adoptNode:(HTMLNode *)node diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index 370155e..7b8314e 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -7,6 +7,7 @@ // #import "HTMLNodeIterator.h" +#import "HTMLDocument.h" #import "HTMLNode.h" typedef NS_ENUM(short, TraverseDirection) @@ -15,6 +16,11 @@ typedef NS_ENUM(short, TraverseDirection) TraverseDirectionPrevious }; +@interface HTMLDocument (Private) +- (void)attachNodeIterator:(HTMLNodeIterator *)iterator; +- (void)detachNodeIterator:(HTMLNodeIterator *)iterator; +@end + @interface HTMLNodeIterator () { HTMLNode *_root; @@ -23,6 +29,8 @@ typedef NS_ENUM(short, TraverseDirection) @implementation HTMLNodeIterator +#pragma mark - Lifecycle + - (instancetype)initWithNode:(HTMLNode *)node { return [self initWithNode:node filter:nil]; @@ -45,10 +53,18 @@ typedef NS_ENUM(short, TraverseDirection) _whatToShow = showOptions; _referenceNode = _root; _pointerBeforeReferenceNode = YES; + [_root.ownerDocument attachNodeIterator:self]; } return self; } +- (void)dealloc +{ + [_root.ownerDocument detachNodeIterator:self]; +} + +#pragma mark - Traversal + - (HTMLNode *)traverseInDirection:(TraverseDirection)direction { HTMLNode *node = self.referenceNode; @@ -129,11 +145,6 @@ NS_INLINE HTMLNodeFilterValue FilterNode(HTMLNodeIterator *iterator, HTMLNode *n return [iterator.filter acceptNode:node]; } -- (id)nextObject -{ - return self.nextNode; -} - - (HTMLNode *)nextNode { return [self traverseInDirection:TraverseDirectionNext]; @@ -144,4 +155,11 @@ NS_INLINE HTMLNodeFilterValue FilterNode(HTMLNodeIterator *iterator, HTMLNode *n return [self traverseInDirection:TraverseDirectionPrevious]; } +#pragma mark - NSEnumerator + +- (id)nextObject +{ + return self.nextNode; +} + @end From d002c6cedd215c98c04c931964074b97fb6faf39 Mon Sep 17 00:00:00 2001 From: iska Date: Sun, 31 May 2015 16:53:54 +0200 Subject: [PATCH 38/55] Add HTML Node methods for comparing node positions in a tree https://dom.spec.whatwg.org/#dom-node-comparedocumentpositionother --- HTMLKit/HTMLNode.h | 15 +++++++++ HTMLKit/HTMLNode.m | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index d9078f2..27ece82 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -25,6 +25,17 @@ typedef NS_ENUM(short, HTMLNodeType) HTMLNodeNotation = 12 // historical }; +typedef NS_ENUM(unsigned short, HTMLDocumentPosition) +{ + HTMLDocumentPositionEquivalent = 0x0, + HTMLDocumentPositionDisconnected = 0x01, + HTMLDocumentPositionPreceding = 0x02, + HTMLDocumentPositionFollowing = 0x04, + HTMLDocumentPositionContains = 0x08, + HTMLDocumentPositionContainedBy = 0x10, + HTMLDocumentPositionImplementationSpecific = 0x20 +}; + @class HTMLDocument; @class HTMLElement; @@ -96,6 +107,10 @@ typedef NS_ENUM(short, HTMLNodeType) - (void)removeAllChildNodes; +- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)node; +- (BOOL)isDescendantOfNode:(HTMLNode *)node; +- (BOOL)containsNode:(HTMLNode *)node; + - (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block; - (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index f65cf9b..ba8cc9a 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -249,6 +249,86 @@ [(NSMutableOrderedSet *)self.childNodes removeAllObjects]; } +- (HTMLDocumentPosition)compareDocumentPositionWithNode:(HTMLNode *)otherNode +{ + if (otherNode == nil) { + return HTMLDocumentPositionDisconnected; + } + + if (self == otherNode) { + return HTMLDocumentPositionEquivalent; + } + + NSArray * (^ ancestorNodes) (HTMLNode *) = ^ NSArray * (HTMLNode *node) { + NSMutableArray *ancestors = [NSMutableArray array]; + for (HTMLNode *node = self; node; node = node.parentNode) { + [ancestors addObject:node]; + } + return ancestors; + }; + + NSArray *ancestors1 = ancestorNodes(self); + NSArray *ancestors2 = ancestorNodes(otherNode); + + if (ancestors1.lastObject != ancestors2.lastObject) { + return HTMLDocumentPositionDisconnected | + HTMLDocumentPositionImplementationSpecific | + HTMLDocumentPositionFollowing; + } + + for (NSUInteger i = MIN(ancestors1.count - 1, ancestors2.count - 1); i; --i) { + HTMLNode *child1 = ancestors1[i]; + HTMLNode *child2 = ancestors2[i]; + + if (child1 != child2) { + for (HTMLNode *sibling = child1.nextSibling; sibling; sibling = sibling.nextSibling) { + if (sibling == child2) { + return HTMLDocumentPositionFollowing; + } + } + return HTMLDocumentPositionPreceding; + } + } + + if (ancestors1.count < ancestors2.count) { + return HTMLDocumentPositionContainedBy | HTMLDocumentPositionFollowing; + } else { + return HTMLDocumentPositionContains | HTMLDocumentPositionPreceding; + } +} + +- (BOOL)isDescendantOfNode:(HTMLNode *)otherNode +{ + if (otherNode == nil) { + return NO; + } + + if (self.ownerDocument != otherNode.ownerDocument) { + return NO; + } + + if (!otherNode.hasChildNodes) { + return NO; + } + + if (otherNode.nodeType == HTMLNodeDocument) { + return self.nodeType != HTMLNodeDocument && self.ownerDocument == otherNode; + } + + for (HTMLNode *parentNode = self.parentNode; parentNode; parentNode = parentNode.parentNode) { + if (parentNode == otherNode) { + return YES; + } + } + + return NO; +} + +- (BOOL)containsNode:(HTMLNode *)otherNode +{ + return self == otherNode || [otherNode isDescendantOfNode:self]; +} + #pragma mark - Enumeration - (void)enumerateChildNodesUsingBlock:(void (^)(HTMLNode *node, NSUInteger idx, BOOL *stop))block From a0fd89f417f52d2493e4eab922b219e0bf474ab6 Mon Sep 17 00:00:00 2001 From: iska Date: Tue, 2 Jun 2015 01:05:44 +0200 Subject: [PATCH 39/55] Move test class for Ordered Dictionary into own group --- HTMLKit.xcodeproj/project.pbxproj | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 1c13dba..18382a7 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -361,7 +361,6 @@ isa = PBXGroup; children = ( 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */, - 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */, 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */, ); @@ -376,6 +375,14 @@ name = Categories; sourceTree = ""; }; + 624E1A2D1B1D1C8A00E66AAC /* Structures */ = { + isa = PBXGroup; + children = ( + 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */, + ); + name = Structures; + sourceTree = ""; + }; 625A14A219C7829400AD0C32 = { isa = PBXGroup; children = ( @@ -451,6 +458,7 @@ 623975581AC362A5007E26F1 /* Tree Construction */, 624B9FB71AE072CB00646C4C /* Nodes */, 624B9FB81AE072D500646C4C /* Categories */, + 624E1A2D1B1D1C8A00E66AAC /* Structures */, 625A14CB19C7829400AD0C32 /* Supporting Files */, ); name = Tests; From 2f1555e93d9970c6f9cbd67b3d4dfac91a67c372 Mon Sep 17 00:00:00 2001 From: iska Date: Tue, 2 Jun 2015 01:06:13 +0200 Subject: [PATCH 40/55] Rename group for DOM tests --- HTMLKit.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 18382a7..4f4907f 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -357,14 +357,14 @@ name = "Tree Construction"; sourceTree = ""; }; - 624B9FB71AE072CB00646C4C /* Nodes */ = { + 624B9FB71AE072CB00646C4C /* DOM */ = { isa = PBXGroup; children = ( 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */, 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */, 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */, ); - name = Nodes; + name = DOM; sourceTree = ""; }; 624B9FB81AE072D500646C4C /* Categories */ = { @@ -456,7 +456,7 @@ children = ( 6236738C1AC0CD2400FF89B3 /* Tokenizer */, 623975581AC362A5007E26F1 /* Tree Construction */, - 624B9FB71AE072CB00646C4C /* Nodes */, + 624B9FB71AE072CB00646C4C /* DOM */, 624B9FB81AE072D500646C4C /* Categories */, 624E1A2D1B1D1C8A00E66AAC /* Structures */, 625A14CB19C7829400AD0C32 /* Supporting Files */, From 0baa343e95677bf843e2d405faf4c34536e86be1 Mon Sep 17 00:00:00 2001 From: iska Date: Fri, 5 Jun 2015 16:46:36 +0200 Subject: [PATCH 41/55] Add Document methods to access the root, head & body elements The implementation will be changed/adapted later when the CSS Selectors are ready. --- HTMLKit/HTMLDocument.h | 6 ++++++ HTMLKit/HTMLDocument.m | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/HTMLKit/HTMLDocument.h b/HTMLKit/HTMLDocument.h index 7c0655f..222ea81 100644 --- a/HTMLKit/HTMLDocument.h +++ b/HTMLKit/HTMLDocument.h @@ -27,6 +27,12 @@ typedef NS_ENUM(short, HTMLDocumentReadyState) @property (nonatomic, assign, readonly) HTMLDocumentReadyState readyState; +@property (nonatomic, strong) HTMLElement *rootElement; + +@property (nonatomic, strong) HTMLElement *head; + +@property (nonatomic, strong) HTMLElement *body; + + (instancetype)documentWithString:(NSString *)string; - (HTMLNode *)adoptNode:(HTMLNode *)node; diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 1837124..912f269 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -8,6 +8,7 @@ #import "HTMLDocument.h" #import "HTMLParser.h" +#import "HTMLNodeIterator.h" #import "HTMLKitDOMExceptions.h" @interface HTMLNode (Private) @@ -66,6 +67,53 @@ } } +#pragma mark - + +- (HTMLElement *)rootElement +{ + for (HTMLNode *node = self.firstChiledNode; node; node = node.nextSibling) { + if (node.nodeType == HTMLNodeElement) { + return node.asElement; + } + } + return nil; +} + +- (void)setRootElement:(HTMLElement *)rootElement +{ + [self replaceChildNode:self.rootElement withNode:rootElement]; +} + +- (HTMLElement *)head +{ + for (HTMLNode *node in [self nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowElement]) { + if ([node.asElement.tagName isEqualToString:@"head"]) { + return node.asElement; + } + } + return nil; +} + +- (void)setHead:(HTMLElement *)head +{ + [self replaceChildNode:self.head withNode:head]; +} + +- (HTMLElement *)body +{ + for (HTMLNode *node in [self nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowElement]) { + if ([node.asElement.tagName isEqualToString:@"body"]) { + return node.asElement; + } + } + return nil; +} + +- (void)setBody:(HTMLElement *)body +{ + [self replaceChildNode:self.body withNode:body]; +} + #pragma mark - Node Iterators - (void)attachNodeIterator:(HTMLNodeIterator *)iterator From d4bb3c6636df003a4ee203a35fbb86b37360683d Mon Sep 17 00:00:00 2001 From: iska Date: Fri, 5 Jun 2015 16:47:37 +0200 Subject: [PATCH 42/55] Add implementation for the removing steps of the HTML Node Iterator https://dom.spec.whatwg.org/#interface-nodeiterator --- HTMLKit/HTMLDocument.m | 17 ++ HTMLKit/HTMLNode.m | 14 ++ HTMLKit/HTMLNodeIterator.m | 40 ++++ HTMLKitTests/HTMLKitNodeIteratorTests.m | 264 +++++++++++++++++++++++- 4 files changed, 333 insertions(+), 2 deletions(-) diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 912f269..9b8d2b3 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -16,6 +16,12 @@ @property (nonatomic, weak) HTMLNode *parentNode; @end +@interface HTMLNodeIterator (Private) +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; +@end + @interface HTMLDocument () { HTMLDocument *_inertTemplateDocument; @@ -126,6 +132,17 @@ [_nodeIterators removeObject:iterator]; } +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling +{ + for (HTMLNodeIterator *iterator in _nodeIterators) { + [iterator runRemovingStepsForNode:oldNode + withOldParent:oldParent + andOldPreviousSibling:oldPreviousSibling]; + } +} + #pragma mark - Mutation Algorithms - (HTMLNode *)adoptNode:(HTMLNode *)node diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index ba8cc9a..bb75fe1 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -14,6 +14,12 @@ #import "HTMLComment.h" #import "HTMLKitDOMExceptions.h" +@interface HTMLDocument (Private) +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling; +@end + @interface HTMLNode () { NSMutableOrderedSet *_childNodes; @@ -224,8 +230,16 @@ NSStringFromSelector(_cmd), child]; } + HTMLNode *oldNode = child; + HTMLNode *oldParent = child.parentNode; + HTMLNode *oldPreviousSibling = child.previousSibling; + [(NSMutableOrderedSet *)self.childNodes removeObject:child]; child.parentNode = nil; + + [self.ownerDocument runRemovingStepsForNode:oldNode + withOldParent:oldParent + andOldPreviousSibling:oldPreviousSibling]; return child; } diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index 7b8314e..4f93445 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -63,6 +63,46 @@ typedef NS_ENUM(short, TraverseDirection) [_root.ownerDocument detachNodeIterator:self]; } +#pragma mark - Removing Steps + +- (void)runRemovingStepsForNode:(HTMLNode *)oldNode + withOldParent:(HTMLNode *)oldParent + andOldPreviousSibling:(HTMLNode *)oldPreviousSibling +{ + if ([oldNode containsNode:_root]) { + return; + } + + if (![oldNode containsNode:_referenceNode]) { + return; + } + + if (_pointerBeforeReferenceNode) { + HTMLNode *nextSibling = oldPreviousSibling != nil ? oldPreviousSibling.nextSibling : oldParent.firstChiledNode; + if (nextSibling != nil) { + _referenceNode = nextSibling; + return; + } + + HTMLNode *next = FollowingNode(oldParent, _root); + if ([_root containsNode:next]) { + _referenceNode = next; + return; + } + + _pointerBeforeReferenceNode = NO; + } + + HTMLNode * (^ lastInclusiveDescendant) (HTMLNode *) = ^ HTMLNode * (HTMLNode *node) { + while (node.lastChildNode) { + node = node.lastChildNode; + } + return node; + }; + + _referenceNode = oldPreviousSibling != nil ? lastInclusiveDescendant(oldPreviousSibling) : oldParent; +} + #pragma mark - Traversal - (HTMLNode *)traverseInDirection:(TraverseDirection)direction diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m index 9eb9c7f..c702e6f 100644 --- a/HTMLKitTests/HTMLKitNodeIteratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -32,7 +32,7 @@ @implementation HTMLKitNodeIteratorTests -#pragma mark - Elements +#pragma mark - Test DOM - (HTMLElement *)div { @@ -133,7 +133,25 @@ return document; } -#pragma mark - Tests +- (HTMLDocument *)document +{ + NSString *htmlString = + @"" + @"" + @"" + @"Title" + @"" + @"" + @"HelloWorld!HTML Kit" + @"

This is an Important paragraph

" + @"" + @""; + + HTMLDocument *document = [HTMLDocument documentWithString:htmlString]; + return document; +} + +#pragma mark - Test Iterator - (void)testNodeIteratorInit { @@ -228,6 +246,8 @@ XCTAssertEqualObjects([result valueForKey:@"name"], expected); } +#pragma mark - Test Iterator ShowOptions (WhatToShow) + - (void)testShowDocument { HTMLDocument *document = self.mixedTree; @@ -301,6 +321,8 @@ XCTAssertEqualObjects([result valueForKey:@"name"], expected); } +#pragma mark - Test Iterator Filter + - (void)testNodeFilter { HTMLDocument *document = self.mixedTree; @@ -315,4 +337,242 @@ XCTAssertEqualObjects([result valueForKey:@"name"], expected); } +#pragma mark - Test Removing Steps + +/* + Test cases for the Removing Steps + https://dom.spec.whatwg.org/#interface-nodeiterator + + Following DOM is used: + +| +| +| +| "Title" +| <body> +| <span> +| "Hello" +| <strong> +| "World!" +| <strong> +| "HTML " +| <!-- This is a Comment! --> +| " Kit" +| <p> +| "This is an " +| <em> +| "Important" +| " paragraph" +*/ + +static void (^ RemoveThenInsertNode)(HTMLNode *) = ^ (HTMLNode *node) { + HTMLNode *parent = node.parentNode; + HTMLNode *nextSibling = node.nextSibling; + [parent removeChildNode:node]; + [parent insertNode:node beforeChildNode:nextSibling]; +}; + +static void (^ IterateUpToNode)(HTMLNodeIterator *, HTMLNode *) = ^ (HTMLNodeIterator *iterator, HTMLNode *target) { + for(HTMLNode *node = iterator.referenceNode; node && (node != target); node = iterator.nextNode); +}; + +static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) { + while (node.lastChildNode) { + node = node.lastChildNode; + } + return node; +}; + +- (void)testThatRemovingRootNodeShouldNotAffectIterator +{ + HTMLDocument *document = self.document; + HTMLNode *node = document.body.firstChiledNode; // <span> + + HTMLNodeIterator *iterator = node.nodeIterator; + + [document.body removeChildNode:node]; + + XCTAssertEqualObjects(iterator.root, node); + XCTAssertEqualObjects(iterator.referenceNode, node); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + XCTAssertEqualObjects(iterator.referenceNode.parentNode, nil); +} + +- (void)testThatRemovingANonInclusiveAnscestorOfReferenceShouldNotAffectIterator +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + [iterator nextNode]; // Reference node: <span> + + RemoveThenInsertNode(iterator.root.childNodes[1]); // Remove <p> + + XCTAssertEqualObjects(iterator.root, body); + XCTAssertEqualObjects(iterator.referenceNode, body.firstChiledNode); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NilOldPreviousSibling +{ + HTMLDocument *document = self.document; + + HTMLNodeIterator *iterator = document.body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + + HTMLNode *node = iterator.nextNode; // Reference node: <span> + RemoveThenInsertNode(node); // Remove <span> with old previos sibling being nil + + XCTAssertEqualObjects(iterator.referenceNode, iterator.root); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); + + HTMLNode *next = iterator.nextNode; // "Hello" + XCTAssertEqualObjects(next, iterator.root.firstChiledNode); +} + +- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_NotBeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + + HTMLNode *node = iterator.root.childNodes[1]; // <p> + IterateUpToNode(iterator, node); // Reference node: <p>, pointer-before-reference: NO + RemoveThenInsertNode(node); // Remove <p> with old previos sibling being <span> + + XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChiledNode)); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +- (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_BeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + + HTMLNode *node = iterator.root.childNodes[1]; // <p> + IterateUpToNode(iterator, node); // Reference node: <p>, pointer-before-reference: NO + [iterator previousNode]; // pointer-before-reference: YES + RemoveThenInsertNode(node); // Remove <p> with old previos sibling being <span> + + XCTAssertEqualObjects(iterator.referenceNode, body.firstChiledNode); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testThatRemovingThenReinsertingReferenceNodeAfterNextShouldReturnItAgain +{ + HTMLDocument *document = self.document; + HTMLNodeIterator *iterator = document.body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + + HTMLNode *node = iterator.nextNode; // <span> + RemoveThenInsertNode(node); + + XCTAssertEqualObjects(iterator.referenceNode, iterator.root); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); + + HTMLNode *next = iterator.nextNode; + XCTAssertEqualObjects(next, node); +} + +- (void)testThatRemovingThenReinsertingReferenceNodeAfterPreviousShouldReturnItAgain +{ + HTMLDocument *document = self.document; + HTMLNodeIterator *iterator = document.body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + [iterator nextNode]; // Reference node: <span> + + HTMLNode *node = iterator.previousNode; // Reference node: <span>, pointer-before-reference: YES + HTMLNode *next = node.nextSibling; // <p> + RemoveThenInsertNode(node); + + XCTAssertEqualObjects(iterator.referenceNode, next); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + + HTMLNode *previous = iterator.previousNode; + XCTAssertEqualObjects(previous, LastDescendant(node)); +} + +- (void)testThatRemovingParentOfReferenceNodeShouldUpdateIterator_NotBeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + HTMLNode *parent = body.childNodes[1]; + + IterateUpToNode(iterator, parent); // Reference node: <p>, pointer-before-reference: NO + [iterator nextNode]; // Reference node: "This is an " + RemoveThenInsertNode(parent); + + XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChiledNode)); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + +- (void)testThatRemovingParentOfReferenceNodeShouldUpdateIterator_BeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + HTMLNode *parent = body.childNodes[1]; + + IterateUpToNode(iterator, parent); // Reference node: <p>, pointer-before-reference: NO + [iterator nextNode]; // Reference node: "This is an " + [iterator previousNode]; // pointer-before-reference: YES + RemoveThenInsertNode(parent); + + XCTAssertEqualObjects(iterator.referenceNode, body.firstChiledNode); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); +} + +- (void)testRemoveReferenceNode_NilPreviousSibling_NonNilParentFirstChild +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + HTMLNodeIterator *iterator = body.nodeIterator; + + [iterator nextNode]; // Reference node: <body> + [iterator nextNode]; // Reference node: <span> + + HTMLNode *node = iterator.previousNode; // Reference node: <span>, pointer-before-reference: YES + XCTAssertNil(node.previousSibling); + XCTAssertNotNil(node.nextSibling); + + HTMLNode *nextSibling = node.nextSibling; // <p> + RemoveThenInsertNode(node); + + XCTAssertEqualObjects(iterator.referenceNode, nextSibling); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); + + HTMLNode *next = iterator.nextNode; // <p> + XCTAssertNotEqualObjects(next, node); + XCTAssertEqualObjects(next, nextSibling); +} + +- (void)testRemoveReferenceNode_NodeAfterOldParentIsOutsideRoot_BeforeReference +{ + HTMLDocument *document = self.document; + HTMLNode *body = document.body; + + body.innerHTML = @"<div><p><a></a></p></div><div></div>"; + + HTMLNodeIterator *iterator = body.firstChiledNode.nodeIterator; + + IterateUpToNode(iterator, LastDescendant(body.firstChiledNode)); // Referecne node: <a> + HTMLNode *node = [iterator previousNode]; // pointer-before-reference: YES + RemoveThenInsertNode(node); + + XCTAssertEqualObjects(iterator.referenceNode, iterator.root.firstChiledNode); + XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); +} + @end From c1b18f527cf1757721970e7fed70f3191aa535bd Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Fri, 5 Jun 2015 16:53:06 +0200 Subject: [PATCH 43/55] Refactor Node Iterator's initializers so that the filter argument is last --- HTMLKit/HTMLDocument.m | 4 ++-- HTMLKit/HTMLNode.h | 4 ++-- HTMLKit/HTMLNode.m | 8 ++++---- HTMLKit/HTMLNodeIterator.h | 5 ++--- HTMLKit/HTMLNodeIterator.m | 4 ++-- HTMLKitTests/HTMLKitNodeIteratorTests.m | 18 +++++++++--------- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 9b8d2b3..a4990dc 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -92,7 +92,7 @@ - (HTMLElement *)head { - for (HTMLNode *node in [self nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowElement]) { + for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { if ([node.asElement.tagName isEqualToString:@"head"]) { return node.asElement; } @@ -107,7 +107,7 @@ - (HTMLElement *)body { - for (HTMLNode *node in [self nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowElement]) { + for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { if ([node.asElement.tagName isEqualToString:@"body"]) { return node.asElement; } diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index 27ece82..c681825 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -116,8 +116,8 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) - (void)enumerateChildElementsUsingBlock:(void (^)(HTMLElement *element, NSUInteger idx, BOOL *stop))block; - (HTMLNodeIterator *)nodeIterator; -- (HTMLNodeIterator *)nodeIteratorWithFilter:(id<HTMLNodeFilter>)filter - showOptions:(HTMLNodeFilterShowOptions)showOptions; +- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id<HTMLNodeFilter>)filter; - (NSString *)treeDescription; @end diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index bb75fe1..c4d166f 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -371,13 +371,13 @@ - (HTMLNodeIterator *)nodeIterator { - return [self nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowAll]; + return [self nodeIteratorWithShowOptions:HTMLNodeFilterShowAll filter:nil]; } -- (HTMLNodeIterator *)nodeIteratorWithFilter:(id<HTMLNodeFilter>)filter - showOptions:(HTMLNodeFilterShowOptions)showOptions +- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id<HTMLNodeFilter>)filter { - return [[HTMLNodeIterator alloc] initWithNode:self filter:filter showOptions:showOptions]; + return [[HTMLNodeIterator alloc] initWithNode:self showOptions:showOptions filter:filter]; } #ifndef HTMLKIT_NO_DOM_CHECKS diff --git a/HTMLKit/HTMLNodeIterator.h b/HTMLKit/HTMLNodeIterator.h index d6c0cb7..ed8bd2b 100644 --- a/HTMLKit/HTMLNodeIterator.h +++ b/HTMLKit/HTMLNodeIterator.h @@ -23,9 +23,8 @@ - (instancetype)initWithNode:(HTMLNode *)node filter:(id<HTMLNodeFilter>)filter; - (instancetype)initWithNode:(HTMLNode *)node - filter:(id<HTMLNodeFilter>)filter - showOptions:(HTMLNodeFilterShowOptions)showOptions; - + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id<HTMLNodeFilter>)filter; - (HTMLNode *)nextNode; - (HTMLNode *)previousNode; diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index 4f93445..a79ae6d 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -39,12 +39,12 @@ typedef NS_ENUM(short, TraverseDirection) - (instancetype)initWithNode:(HTMLNode *)node filter:(id<HTMLNodeFilter>)filter { - return [self initWithNode:node filter:nil showOptions:HTMLNodeFilterShowAll]; + return [self initWithNode:node showOptions:HTMLNodeFilterShowAll filter:filter]; } - (instancetype)initWithNode:(HTMLNode *)node - filter:(id<HTMLNodeFilter>)filter showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id<HTMLNodeFilter>)filter { self = [super init]; if (self) { diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m index c702e6f..4fadae1 100644 --- a/HTMLKitTests/HTMLKitNodeIteratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -252,7 +252,7 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowDocument]; + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowDocument filter:nil]; NSArray *result = iterator.allObjects; NSArray *expected = @[@"#document"]; XCTAssertEqual(result.count, 1); @@ -264,7 +264,7 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowDocumentType]; + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowDocumentType filter:nil]; NSArray *result = iterator.allObjects; NSArray *expected = @[@"html"]; XCTAssertEqual(result.count, 1); @@ -276,7 +276,7 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowComment]; + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowComment filter:nil]; NSArray *result = iterator.allObjects; NSArray *expected = @[@"#comment", @"#comment"]; XCTAssertEqual(result.count, 2); @@ -289,7 +289,7 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowText]; + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowText filter:nil]; NSArray *result = iterator.allObjects; NSArray *expected = @[@"#text"]; XCTAssertEqual(result.count, 1); @@ -301,7 +301,7 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil showOptions:HTMLNodeFilterShowElement]; + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]; NSArray *result = iterator.allObjects; NSArray *expected = @[@"html", @"div"]; XCTAssertEqual(result.count, 2); @@ -313,8 +313,8 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:nil - showOptions:HTMLNodeFilterShowElement | HTMLNodeFilterShowText]; + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowElement | HTMLNodeFilterShowText + filter:nil]; NSArray *result = iterator.allObjects; NSArray *expected = @[@"html", @"#text", @"div"]; XCTAssertEqual(result.count, 3); @@ -327,8 +327,8 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithFilter:[CommentNodeFilter new] - showOptions:HTMLNodeFilterShowAll]; + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowAll + filter:[CommentNodeFilter new]]; NSArray *result = iterator.allObjects; NSArray *expected = @[@"#comment"]; From dc3de7a47091d6f865690e063d2bde488ad45a75 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Fri, 5 Jun 2015 17:05:22 +0200 Subject: [PATCH 44/55] Remove the HTMLNodeFilterValue enum since Reject and Skip are semantically equivalent --- HTMLKit/HTMLNodeFilter.h | 9 +-------- HTMLKit/HTMLNodeIterator.m | 8 ++++---- HTMLKitTests/HTMLKitNodeIteratorTests.m | 6 +++--- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h index 021fe5a..40785a3 100644 --- a/HTMLKit/HTMLNodeFilter.h +++ b/HTMLKit/HTMLNodeFilter.h @@ -8,13 +8,6 @@ #import <Foundation/Foundation.h> -typedef NS_ENUM(unsigned short, HTMLNodeFilterValue) -{ - HTMLNodeFilterAccept = 1, - HTMLNodeFilterReject = 2, - HTMLNodeFilterSkip = 3 -}; - typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) { HTMLNodeFilterShowAll = 0xFFFFFFFF, @@ -30,6 +23,6 @@ typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) @protocol HTMLNodeFilter <NSObject> -- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node; +- (BOOL)acceptNode:(HTMLNode *)node; @end diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index a79ae6d..a23de3d 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -128,7 +128,7 @@ typedef NS_ENUM(short, TraverseDirection) } beforeNode = YES; } - } while (FilterNode(self, node) != HTMLNodeFilterAccept); + } while (!FilterNode(self, node)); _referenceNode = node; _pointerBeforeReferenceNode = beforeNode; @@ -171,15 +171,15 @@ NS_INLINE HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root) return nil; } -NS_INLINE HTMLNodeFilterValue FilterNode(HTMLNodeIterator *iterator, HTMLNode *node) +NS_INLINE BOOL FilterNode(HTMLNodeIterator *iterator, HTMLNode *node) { unsigned long nthBit = (1 << (node.nodeType - 1)) & iterator.whatToShow; if (!nthBit) { - return HTMLNodeFilterSkip; + return NO; } if (iterator.filter == nil) { - return HTMLNodeFilterAccept; + return YES; } return [iterator.filter acceptNode:node]; diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m index 4fadae1..a9b05f2 100644 --- a/HTMLKitTests/HTMLKitNodeIteratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -14,14 +14,14 @@ @implementation CommentNodeFilter -- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node +- (BOOL)acceptNode:(HTMLNode *)node { if (node.nodeType == HTMLNodeComment) { if ([[(HTMLComment *)node data] rangeOfString:@"second"].location != NSNotFound) { - return HTMLNodeFilterAccept; + return YES; } } - return HTMLNodeFilterReject; + return NO; } @end From 40400864d8dae48c5933894e895c5e33a00b467b Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Fri, 5 Jun 2015 18:06:23 +0200 Subject: [PATCH 45/55] Add block-based API for Node Filter - HTMLNodeFilterBlock is a block-based class implementation conforming to HTMLNodeFilter - HTMLNode & NodeIterator get block-based initializers --- HTMLKit.xcodeproj/project.pbxproj | 6 ++++ HTMLKit/HTMLNode.h | 3 ++ HTMLKit/HTMLNode.m | 6 ++++ HTMLKit/HTMLNodeFilter.h | 6 ++++ HTMLKit/HTMLNodeFilter.m | 42 +++++++++++++++++++++++++ HTMLKit/HTMLNodeIterator.h | 4 +++ HTMLKit/HTMLNodeIterator.m | 11 +++++++ HTMLKitTests/HTMLKitNodeIteratorTests.m | 27 +++++----------- 8 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 HTMLKit/HTMLNodeFilter.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 4f4907f..350eb6c 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -66,6 +66,8 @@ 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; }; 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; + 624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; + 624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; }; 6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; }; 6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; }; @@ -211,6 +213,7 @@ 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerStates.h; sourceTree = "<group>"; }; 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLToken.h; sourceTree = "<group>"; }; 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLToken.m; sourceTree = "<group>"; }; + 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeFilter.m; sourceTree = "<group>"; }; 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = "<group>"; }; 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = "<group>"; }; 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; sourceTree = "<group>"; }; @@ -338,6 +341,7 @@ 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */, 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */, 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */, + 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */, 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */, 6238C9841AB8D6330006512E /* HTMLKitDOMExceptions.m */, 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, @@ -755,6 +759,7 @@ 625A150A19C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */, 623857931A9E772B003A45D9 /* HTMLDocument.m in Sources */, 623406E81ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */, + 624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */, 624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */, 625A14BE19C7829400AD0C32 /* HTMLKit.m in Sources */, 623857981A9E8606003A45D9 /* HTMLDocumentType.m in Sources */, @@ -807,6 +812,7 @@ 625A150919C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */, 623857921A9E772B003A45D9 /* HTMLDocument.m in Sources */, 623406E71ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */, + 624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */, 624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */, 625A150219C783DE00AD0C32 /* HTMLKit.m in Sources */, 623857971A9E8606003A45D9 /* HTMLDocumentType.m in Sources */, diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index c681825..ac4a20c 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -118,6 +118,9 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) - (HTMLNodeIterator *)nodeIterator; - (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions filter:(id<HTMLNodeFilter>)filter; +- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions + filterBlock:(BOOL (^)(HTMLNode *node))filter; + - (NSString *)treeDescription; @end diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index c4d166f..6f4a221 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -380,6 +380,12 @@ return [[HTMLNodeIterator alloc] initWithNode:self showOptions:showOptions filter:filter]; } +- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions + filterBlock:(BOOL (^)(HTMLNode *node))filter +{ + return [HTMLNodeIterator iteratorWithNode:self showOptions:showOptions filter:filter]; +} + #ifndef HTMLKIT_NO_DOM_CHECKS #pragma mark - Validity Checks diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h index 40785a3..84c4c57 100644 --- a/HTMLKit/HTMLNodeFilter.h +++ b/HTMLKit/HTMLNodeFilter.h @@ -26,3 +26,9 @@ typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) - (BOOL)acceptNode:(HTMLNode *)node; @end + +@interface HTMLNodeFilterBlock : NSObject <HTMLNodeFilter> + ++ (instancetype)filterWithBlock:(BOOL (^)(HTMLNode *node))block; + +@end \ No newline at end of file diff --git a/HTMLKit/HTMLNodeFilter.m b/HTMLKit/HTMLNodeFilter.m new file mode 100644 index 0000000..914ee72 --- /dev/null +++ b/HTMLKit/HTMLNodeFilter.m @@ -0,0 +1,42 @@ +// +// HTMLNodeFilter.m +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNodeFilter.h" + +@interface HTMLNodeFilterBlock () +{ + BOOL (^ _block)(HTMLNode *); +} +@end + +@implementation HTMLNodeFilterBlock + ++ (instancetype)filterWithBlock:(BOOL (^)(HTMLNode *))block +{ + return [[self alloc] initWithBlock:block]; +} + +- (instancetype)initWithBlock:(BOOL (^)(HTMLNode *))block +{ + self = [super init]; + if (self) { + _block = [block copy]; + } + return self; +} + +- (BOOL)acceptNode:(HTMLNode *)node +{ + if (!_block) { + return NO; + } + + return _block(node); +} + +@end diff --git a/HTMLKit/HTMLNodeIterator.h b/HTMLKit/HTMLNodeIterator.h index ed8bd2b..803f55e 100644 --- a/HTMLKit/HTMLNodeIterator.h +++ b/HTMLKit/HTMLNodeIterator.h @@ -19,6 +19,10 @@ @property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow; @property (nonatomic, strong, readonly) id<HTMLNodeFilter> filter; ++ (instancetype)iteratorWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(BOOL (^)(HTMLNode *node))filter; + - (instancetype)initWithNode:(HTMLNode *)node; - (instancetype)initWithNode:(HTMLNode *)node filter:(id<HTMLNodeFilter>)filter; diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index a23de3d..1f72bed 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -10,6 +10,8 @@ #import "HTMLDocument.h" #import "HTMLNode.h" +#import <objc/runtime.h> + typedef NS_ENUM(short, TraverseDirection) { TraverseDirectionNext, @@ -31,6 +33,15 @@ typedef NS_ENUM(short, TraverseDirection) #pragma mark - Lifecycle ++ (instancetype)iteratorWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(BOOL (^)(HTMLNode *))filter +{ + return [[self alloc] initWithNode:node + showOptions:showOptions + filter:[HTMLNodeFilterBlock filterWithBlock:filter]]; +} + - (instancetype)initWithNode:(HTMLNode *)node { return [self initWithNode:node filter:nil]; diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m index a9b05f2..a6411f3 100644 --- a/HTMLKitTests/HTMLKitNodeIteratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -9,23 +9,6 @@ #import <XCTest/XCTest.h> #import "HTMLDOM.h" -@interface CommentNodeFilter : NSObject <HTMLNodeFilter> -@end - -@implementation CommentNodeFilter - -- (BOOL)acceptNode:(HTMLNode *)node -{ - if (node.nodeType == HTMLNodeComment) { - if ([[(HTMLComment *)node data] rangeOfString:@"second"].location != NSNotFound) { - return YES; - } - } - return NO; -} - -@end - @interface HTMLKitNodeIteratorTests : XCTestCase @end @@ -327,8 +310,14 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowAll - filter:[CommentNodeFilter new]]; + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowAll filterBlock:^BOOL(HTMLNode *node) { + if (node.nodeType == HTMLNodeComment) { + if ([[(HTMLComment *)node data] rangeOfString:@"second"].location != NSNotFound) { + return YES; + } + } + return NO; + }]; NSArray *result = iterator.allObjects; NSArray *expected = @[@"#comment"]; From 0d7c57d75506d75f0c4c2b1a67bd90e31fcf9537 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Fri, 5 Jun 2015 22:09:02 +0200 Subject: [PATCH 46/55] Refactor common node traversal methods into separate class These will be used in the Tree Walker https://dom.spec.whatwg.org/#interface-treewalker --- HTMLKit.xcodeproj/project.pbxproj | 10 +++++ HTMLKit/HTMLNodeIterator.m | 56 ++-------------------------- HTMLKit/HTMLNodeTraversal.h | 16 ++++++++ HTMLKit/HTMLNodeTraversal.m | 61 +++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 53 deletions(-) create mode 100644 HTMLKit/HTMLNodeTraversal.h create mode 100644 HTMLKit/HTMLNodeTraversal.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 350eb6c..4e109d3 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -66,6 +66,9 @@ 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; }; 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */; }; + 624717181B22333200C11912 /* HTMLNodeTraversal.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717161B22333200C11912 /* HTMLNodeTraversal.h */; }; + 624717191B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; }; + 6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; }; 624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; 624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; }; @@ -213,6 +216,8 @@ 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLTokenizerStates.h; sourceTree = "<group>"; }; 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLToken.h; sourceTree = "<group>"; }; 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLToken.m; sourceTree = "<group>"; }; + 624717161B22333200C11912 /* HTMLNodeTraversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTraversal.h; sourceTree = "<group>"; }; + 624717171B22333200C11912 /* HTMLNodeTraversal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTraversal.m; sourceTree = "<group>"; }; 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeFilter.m; sourceTree = "<group>"; }; 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = "<group>"; }; 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = "<group>"; }; @@ -340,6 +345,8 @@ 623406E01ADB04F9004677A3 /* HTMLTemplate.m */, 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */, 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */, + 624717161B22333200C11912 /* HTMLNodeTraversal.h */, + 624717171B22333200C11912 /* HTMLNodeTraversal.m */, 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */, 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */, 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */, @@ -553,6 +560,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 624717181B22333200C11912 /* HTMLNodeTraversal.h in Headers */, 6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */, 62D89DB11AE7081600B6243D /* HTMLTokenizerStates.h in Headers */, 62D89DB21AE7081600B6243D /* HTMLTokenizerCharacters.h in Headers */, @@ -753,6 +761,7 @@ 62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, 623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */, 6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */, + 6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */, 62F873ED19E088C90062683C /* HTMLParser.m in Sources */, 623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */, 6279F87619E1808D00F12EE5 /* HTMLElement.m in Sources */, @@ -806,6 +815,7 @@ 62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, 623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */, 6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */, + 624717191B22333200C11912 /* HTMLNodeTraversal.m in Sources */, 62F873EC19E088C90062683C /* HTMLParser.m in Sources */, 623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */, 6279F87519E1808D00F12EE5 /* HTMLElement.m in Sources */, diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index 1f72bed..2927ac0 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -9,8 +9,8 @@ #import "HTMLNodeIterator.h" #import "HTMLDocument.h" #import "HTMLNode.h" - -#import <objc/runtime.h> +#import "HTMLNodeFilter.h" +#import "HTMLNodeTraversal.h" typedef NS_ENUM(short, TraverseDirection) { @@ -139,63 +139,13 @@ typedef NS_ENUM(short, TraverseDirection) } beforeNode = YES; } - } while (!FilterNode(self, node)); + } while (!FilterNode(self.filter, self.whatToShow, node)); _referenceNode = node; _pointerBeforeReferenceNode = beforeNode; return node; } -NS_INLINE HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root) -{ - HTMLNode *previous = node.previousSibling; - if (previous != nil) { - while (previous.lastChildNode != nil) { - previous = previous.lastChildNode; - } - return previous; - } - - if (node == root) { - return nil; - } - - return node.parentNode; -} - -NS_INLINE HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root) -{ - if (node.firstChiledNode != nil) { - return node.firstChiledNode; - } - - do { - if (node == root) { - return nil; - } - if (node.nextSibling != nil) { - return node.nextSibling; - } - node = node.parentNode; - } while (node != nil); - - return nil; -} - -NS_INLINE BOOL FilterNode(HTMLNodeIterator *iterator, HTMLNode *node) -{ - unsigned long nthBit = (1 << (node.nodeType - 1)) & iterator.whatToShow; - if (!nthBit) { - return NO; - } - - if (iterator.filter == nil) { - return YES; - } - - return [iterator.filter acceptNode:node]; -} - - (HTMLNode *)nextNode { return [self traverseInDirection:TraverseDirectionNext]; diff --git a/HTMLKit/HTMLNodeTraversal.h b/HTMLKit/HTMLNodeTraversal.h new file mode 100644 index 0000000..b3fe2f6 --- /dev/null +++ b/HTMLKit/HTMLNodeTraversal.h @@ -0,0 +1,16 @@ +// +// HTMLNodeTraversal.h +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "HTMLNodeFilter.h" + +@class HTMLNode; + +extern HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root); +extern HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root); +extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node); diff --git a/HTMLKit/HTMLNodeTraversal.m b/HTMLKit/HTMLNodeTraversal.m new file mode 100644 index 0000000..6f8b5eb --- /dev/null +++ b/HTMLKit/HTMLNodeTraversal.m @@ -0,0 +1,61 @@ +// +// HTMLNodeTraversal.m +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLNodeTraversal.h" +#import "HTMLNode.h" +#import "HTMLNodeFilter.h" + +HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root) +{ + HTMLNode *previous = node.previousSibling; + if (previous != nil) { + while (previous.lastChildNode != nil) { + previous = previous.lastChildNode; + } + return previous; + } + + if (node == root) { + return nil; + } + + return node.parentNode; +} + +HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root) +{ + if (node.firstChiledNode != nil) { + return node.firstChiledNode; + } + + do { + if (node == root) { + return nil; + } + if (node.nextSibling != nil) { + return node.nextSibling; + } + node = node.parentNode; + } while (node != nil); + + return nil; +} + +extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node) +{ + unsigned long nthBit = (1 << (node.nodeType - 1)) & whatToShow; + if (!nthBit) { + return NO; + } + + if (filter == nil) { + return YES; + } + + return [filter acceptNode:node]; +} From 0c82f6891b3d56cf2d09d7a2162c2f548bfb6e99 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Fri, 5 Jun 2015 22:46:41 +0200 Subject: [PATCH 47/55] Add implementation for the Tree Walker https://dom.spec.whatwg.org/#interface-treewalker --- HTMLKit.xcodeproj/project.pbxproj | 10 ++ HTMLKit/HTMLNodeTraversal.h | 1 + HTMLKit/HTMLNodeTraversal.m | 17 ++- HTMLKit/HTMLTreeWalker.h | 29 ++++ HTMLKit/HTMLTreeWalker.m | 222 ++++++++++++++++++++++++++++++ 5 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 HTMLKit/HTMLTreeWalker.h create mode 100644 HTMLKit/HTMLTreeWalker.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 4e109d3..ff7ddb5 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -71,6 +71,9 @@ 6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; }; 624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; 624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; + 624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717BA1B22009200B38302 /* HTMLTreeWalker.h */; }; + 624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; }; + 624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717BB1B22009200B38302 /* HTMLTreeWalker.m */; }; 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; }; 6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; }; 6247A9451B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */; }; @@ -219,6 +222,8 @@ 624717161B22333200C11912 /* HTMLNodeTraversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTraversal.h; sourceTree = "<group>"; }; 624717171B22333200C11912 /* HTMLNodeTraversal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTraversal.m; sourceTree = "<group>"; }; 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeFilter.m; sourceTree = "<group>"; }; + 624717BA1B22009200B38302 /* HTMLTreeWalker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTreeWalker.h; sourceTree = "<group>"; }; + 624717BB1B22009200B38302 /* HTMLTreeWalker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalker.m; sourceTree = "<group>"; }; 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeIterator.h; sourceTree = "<group>"; }; 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeIterator.m; sourceTree = "<group>"; }; 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeFilter.h; sourceTree = "<group>"; }; @@ -345,6 +350,8 @@ 623406E01ADB04F9004677A3 /* HTMLTemplate.m */, 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */, 6247A9421B152F4F00CCF25C /* HTMLNodeIterator.m */, + 624717BA1B22009200B38302 /* HTMLTreeWalker.h */, + 624717BB1B22009200B38302 /* HTMLTreeWalker.m */, 624717161B22333200C11912 /* HTMLNodeTraversal.h */, 624717171B22333200C11912 /* HTMLNodeTraversal.m */, 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */, @@ -596,6 +603,7 @@ 623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */, 623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */, 62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */, + 624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */, 62362A3C1A9FA70400301989 /* HTMLText.h in Headers */, 6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */, ); @@ -755,6 +763,7 @@ 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, 6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */, 6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */, + 624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */, 62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */, 62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */, @@ -809,6 +818,7 @@ 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, 6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */, 623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */, + 624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */, 62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */, 62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */, diff --git a/HTMLKit/HTMLNodeTraversal.h b/HTMLKit/HTMLNodeTraversal.h index b3fe2f6..610700f 100644 --- a/HTMLKit/HTMLNodeTraversal.h +++ b/HTMLKit/HTMLNodeTraversal.h @@ -13,4 +13,5 @@ extern HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root); extern HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root); +extern HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root); extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node); diff --git a/HTMLKit/HTMLNodeTraversal.m b/HTMLKit/HTMLNodeTraversal.m index 6f8b5eb..1ef2de8 100644 --- a/HTMLKit/HTMLNodeTraversal.m +++ b/HTMLKit/HTMLNodeTraversal.m @@ -46,7 +46,22 @@ HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root) return nil; } -extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node) +HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root) +{ + do { + if (node == root) { + return nil; + } + if (node.nextSibling != nil) { + return node.nextSibling; + } + node = node.parentNode; + } while (node != nil); + + return nil; +} + +BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node) { unsigned long nthBit = (1 << (node.nodeType - 1)) & whatToShow; if (!nthBit) { diff --git a/HTMLKit/HTMLTreeWalker.h b/HTMLKit/HTMLTreeWalker.h new file mode 100644 index 0000000..8b05394 --- /dev/null +++ b/HTMLKit/HTMLTreeWalker.h @@ -0,0 +1,29 @@ +// +// HTMLTreeWalker.h +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "HTMLNodeFilter.h" + +@class HTMLNode; + +@interface HTMLTreeWalker : NSObject + +@property (nonatomic, strong, readonly) HTMLNode *root; +@property (nonatomic, assign, readonly) HTMLNodeFilterShowOptions whatToShow; +@property (nonatomic, strong, readonly) id<HTMLNodeFilter> filter; +@property (nonatomic, strong) HTMLNode *currentNode; + +- (HTMLNode *)parentNode; +- (HTMLNode *)firstChild; +- (HTMLNode *)lastChild; +- (HTMLNode *)previousSibling; +- (HTMLNode *)nextSibling; +- (HTMLNode *)previousNode; +- (HTMLNode *)nextNode; + +@end diff --git a/HTMLKit/HTMLTreeWalker.m b/HTMLKit/HTMLTreeWalker.m new file mode 100644 index 0000000..d4a90c5 --- /dev/null +++ b/HTMLKit/HTMLTreeWalker.m @@ -0,0 +1,222 @@ +// +// HTMLTreeWalker.m +// HTMLKit +// +// Created by Iska on 05/06/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLTreeWalker.h" +#import "HTMLNode.h" +#import "HTMLNodeFilter.h" +#import "HTMLNodeTraversal.h" + +typedef NS_ENUM(short, HTMLTreeWalkerChildrenType) +{ + HTMLTreeWalkerChildrenTypeFirst, + HTMLTreeWalkerChildrenTypeLast +}; + +typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) +{ + HTMLTreeWalkerSiblingsTypeNext, + HTMLTreeWalkerSiblingsTypePrevious +}; + +@implementation HTMLTreeWalker + +#pragma mark - Lifecycle + +- (instancetype)initWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id<HTMLNodeFilter>)filter +{ + self = [super init]; + if (self) { + _root = node; + _filter = filter; + _whatToShow = showOptions; + _currentNode = _root; + } + return self; +} + +#pragma mark - Traversal + +- (HTMLNode *)traverseChildrenOfType:(HTMLTreeWalkerChildrenType)type +{ + HTMLNode *node = _currentNode; + + node = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode; + + while (node != nil) { + BOOL result = FilterNode(self.filter, self.whatToShow, node); + if (result) { + _currentNode = node; + return node; + } + + HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode; + if (child != nil) { + node = child; + continue; + } + + while (node != nil) { + HTMLNode *sibling = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.nextSibling : node.previousSibling; + if (sibling != nil) { + node = sibling; + continue; + } + + HTMLNode *parent = node.parentNode; + if (parent == nil || parent == _root || parent == _currentNode) { + return nil; + } + node = parent; + } + } + + return nil; +} + +- (HTMLNode *)parentNode +{ + HTMLNode *node = _currentNode; + + while (node != nil && node != _root) { + node = node.parentNode; + if (node != nil && FilterNode(self.filter, self.whatToShow, node)) { + _currentNode = node; + return node; + } + } + return nil; +} + +- (HTMLNode *)firstChild +{ + return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeFirst]; +} + +- (HTMLNode *)lastChild +{ + return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeLast]; +} + +- (HTMLNode *)traverseSiblingsOfType:(HTMLTreeWalkerSiblingsType)type +{ + HTMLNode *node = _currentNode; + + if (node == _root) { + return nil; + } + + while (YES) { + HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling; + while (sibling != nil) { + node = sibling; + BOOL result = FilterNode(self.filter, self.whatToShow, node); + if (result) { + _currentNode = node; + return node; + } + + HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChiledNode : node.lastChildNode; + if (sibling == nil) { + sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling; + } + } + + node = node.parentNode; + if (node == nil || node == _root) { + return nil; + } + + if (FilterNode(self.filter, self.whatToShow, node)) { + return nil; + } + } + return nil; +} + +- (HTMLNode *)previousSibling +{ + return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypePrevious]; +} + +- (HTMLNode *)nextSibling +{ + return [self traverseSiblingsOfType:HTMLTreeWalkerSiblingsTypeNext]; +} + +- (HTMLNode *)previousNode +{ + HTMLNode *node = _currentNode; + + while (node != _root) { + HTMLNode *sibling = node.previousSibling; + + while (sibling != nil) { + node = sibling; + + BOOL result = FilterNode(self.filter, self.whatToShow, node); + while (result && node.hasChildNodes) { + node = node.lastChildNode; + result = FilterNode(self.filter, self.whatToShow, node); + } + + if (result) { + _currentNode = node; + return node; + } + + sibling = node.previousSibling; + } + + if (node == _root || node.parentNode == nil) { + return nil; + } + + node = node.parentNode; + if (FilterNode(self.filter, self.whatToShow, node)) { + _currentNode = node; + return node; + } + } + + return nil; +} + +- (HTMLNode *)nextNode +{ + HTMLNode *node = _currentNode; + + BOOL result = YES; + + while (YES) { + while (!result && node.hasChildNodes) { + node = node.firstChiledNode; + result = FilterNode(self.filter, self.whatToShow, node); + if (result) { + _currentNode = node; + return node; + } + } + + HTMLNode *nextSibling; + while ((nextSibling = FollowingNodeSkippingChildren(node, _root)) != nil) { + node = nextSibling; + result = FilterNode(self.filter, self.whatToShow, node); + if (result) { + _currentNode = node; + return node; + } + break; + } + } + + return nil; +} + +@end From a390edf59979290237d6fa6b4e14ad3eaeff2463 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Fri, 5 Jun 2015 22:54:37 +0200 Subject: [PATCH 48/55] Reintroduce the HTMLNodeFilterValue for Tree Walker implementation While the Node Iterator treats skip & reject the same way, the Tree Walker can skip over a node and all its children if rejected. This reverses dc3de7a47091d6f865690e063d2bde488ad45a75 --- HTMLKit/HTMLNode.h | 2 +- HTMLKit/HTMLNode.m | 2 +- HTMLKit/HTMLNodeFilter.h | 13 +++-- HTMLKit/HTMLNodeFilter.m | 8 +-- HTMLKit/HTMLNodeIterator.h | 2 +- HTMLKit/HTMLNodeIterator.m | 4 +- HTMLKit/HTMLNodeTraversal.h | 2 +- HTMLKit/HTMLNodeTraversal.m | 6 +-- HTMLKit/HTMLTreeWalker.m | 65 +++++++++++++------------ HTMLKitTests/HTMLKitNodeIteratorTests.m | 7 +-- 10 files changed, 61 insertions(+), 50 deletions(-) diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index ac4a20c..a488dcd 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -119,7 +119,7 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) - (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions filter:(id<HTMLNodeFilter>)filter; - (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions - filterBlock:(BOOL (^)(HTMLNode *node))filter; + filterBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))filter; - (NSString *)treeDescription; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index 6f4a221..f6cedbc 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -381,7 +381,7 @@ } - (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions - filterBlock:(BOOL (^)(HTMLNode *node))filter + filterBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))filter { return [HTMLNodeIterator iteratorWithNode:self showOptions:showOptions filter:filter]; } diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h index 84c4c57..9df0e74 100644 --- a/HTMLKit/HTMLNodeFilter.h +++ b/HTMLKit/HTMLNodeFilter.h @@ -8,6 +8,13 @@ #import <Foundation/Foundation.h> +typedef NS_ENUM(unsigned short, HTMLNodeFilterValue) +{ + HTMLNodeFilterAccept = 1, + HTMLNodeFilterReject = 2, + HTMLNodeFilterSkip = 3 +}; + typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) { HTMLNodeFilterShowAll = 0xFFFFFFFF, @@ -23,12 +30,12 @@ typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) @protocol HTMLNodeFilter <NSObject> -- (BOOL)acceptNode:(HTMLNode *)node; +- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node; @end @interface HTMLNodeFilterBlock : NSObject <HTMLNodeFilter> -+ (instancetype)filterWithBlock:(BOOL (^)(HTMLNode *node))block; ++ (instancetype)filterWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))block; -@end \ No newline at end of file +@end diff --git a/HTMLKit/HTMLNodeFilter.m b/HTMLKit/HTMLNodeFilter.m index 914ee72..96e4843 100644 --- a/HTMLKit/HTMLNodeFilter.m +++ b/HTMLKit/HTMLNodeFilter.m @@ -16,12 +16,12 @@ @implementation HTMLNodeFilterBlock -+ (instancetype)filterWithBlock:(BOOL (^)(HTMLNode *))block ++ (instancetype)filterWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *))block { return [[self alloc] initWithBlock:block]; } -- (instancetype)initWithBlock:(BOOL (^)(HTMLNode *))block +- (instancetype)initWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *))block { self = [super init]; if (self) { @@ -30,10 +30,10 @@ return self; } -- (BOOL)acceptNode:(HTMLNode *)node +- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node { if (!_block) { - return NO; + return HTMLNodeFilterSkip; } return _block(node); diff --git a/HTMLKit/HTMLNodeIterator.h b/HTMLKit/HTMLNodeIterator.h index 803f55e..6dd269e 100644 --- a/HTMLKit/HTMLNodeIterator.h +++ b/HTMLKit/HTMLNodeIterator.h @@ -21,7 +21,7 @@ + (instancetype)iteratorWithNode:(HTMLNode *)node showOptions:(HTMLNodeFilterShowOptions)showOptions - filter:(BOOL (^)(HTMLNode *node))filter; + filter:(HTMLNodeFilterValue (^)(HTMLNode *node))filter; - (instancetype)initWithNode:(HTMLNode *)node; - (instancetype)initWithNode:(HTMLNode *)node diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index 2927ac0..c5e85d1 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -35,7 +35,7 @@ typedef NS_ENUM(short, TraverseDirection) + (instancetype)iteratorWithNode:(HTMLNode *)node showOptions:(HTMLNodeFilterShowOptions)showOptions - filter:(BOOL (^)(HTMLNode *))filter + filter:(HTMLNodeFilterValue (^)(HTMLNode *))filter { return [[self alloc] initWithNode:node showOptions:showOptions @@ -139,7 +139,7 @@ typedef NS_ENUM(short, TraverseDirection) } beforeNode = YES; } - } while (!FilterNode(self.filter, self.whatToShow, node)); + } while (FilterNode(self.filter, self.whatToShow, node) != HTMLNodeFilterAccept); _referenceNode = node; _pointerBeforeReferenceNode = beforeNode; diff --git a/HTMLKit/HTMLNodeTraversal.h b/HTMLKit/HTMLNodeTraversal.h index 610700f..ee2bf7e 100644 --- a/HTMLKit/HTMLNodeTraversal.h +++ b/HTMLKit/HTMLNodeTraversal.h @@ -14,4 +14,4 @@ extern HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root); extern HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root); extern HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root); -extern BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node); +extern HTMLNodeFilterValue FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node); diff --git a/HTMLKit/HTMLNodeTraversal.m b/HTMLKit/HTMLNodeTraversal.m index 1ef2de8..1c646df 100644 --- a/HTMLKit/HTMLNodeTraversal.m +++ b/HTMLKit/HTMLNodeTraversal.m @@ -61,15 +61,15 @@ HTMLNode * FollowingNodeSkippingChildren(HTMLNode *node, HTMLNode *root) return nil; } -BOOL FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node) +HTMLNodeFilterValue FilterNode(id<HTMLNodeFilter> filter, HTMLNodeFilterShowOptions whatToShow, HTMLNode *node) { unsigned long nthBit = (1 << (node.nodeType - 1)) & whatToShow; if (!nthBit) { - return NO; + return HTMLNodeFilterSkip; } if (filter == nil) { - return YES; + return HTMLNodeFilterAccept; } return [filter acceptNode:node]; diff --git a/HTMLKit/HTMLTreeWalker.m b/HTMLKit/HTMLTreeWalker.m index d4a90c5..b76c119 100644 --- a/HTMLKit/HTMLTreeWalker.m +++ b/HTMLKit/HTMLTreeWalker.m @@ -43,6 +43,20 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) #pragma mark - Traversal +- (HTMLNode *)parentNode +{ + HTMLNode *node = _currentNode; + + while (node != nil && node != _root) { + node = node.parentNode; + if (node != nil && FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) { + _currentNode = node; + return node; + } + } + return nil; +} + - (HTMLNode *)traverseChildrenOfType:(HTMLTreeWalkerChildrenType)type { HTMLNode *node = _currentNode; @@ -50,16 +64,18 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) node = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode; while (node != nil) { - BOOL result = FilterNode(self.filter, self.whatToShow, node); - if (result) { + HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node); + if (result == HTMLNodeFilterAccept) { _currentNode = node; return node; } - HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode; - if (child != nil) { - node = child; - continue; + if (result == HTMLNodeFilterSkip) { + HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode; + if (child != nil) { + node = child; + continue; + } } while (node != nil) { @@ -80,20 +96,6 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) return nil; } -- (HTMLNode *)parentNode -{ - HTMLNode *node = _currentNode; - - while (node != nil && node != _root) { - node = node.parentNode; - if (node != nil && FilterNode(self.filter, self.whatToShow, node)) { - _currentNode = node; - return node; - } - } - return nil; -} - - (HTMLNode *)firstChild { return [self traverseChildrenOfType:HTMLTreeWalkerChildrenTypeFirst]; @@ -116,8 +118,8 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling; while (sibling != nil) { node = sibling; - BOOL result = FilterNode(self.filter, self.whatToShow, node); - if (result) { + HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node); + if (result == HTMLNodeFilterAccept) { _currentNode = node; return node; } @@ -133,10 +135,11 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) return nil; } - if (FilterNode(self.filter, self.whatToShow, node)) { + if (FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) { return nil; } } + return nil; } @@ -160,13 +163,13 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) while (sibling != nil) { node = sibling; - BOOL result = FilterNode(self.filter, self.whatToShow, node); - while (result && node.hasChildNodes) { + HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node); + while (result != HTMLNodeFilterReject && node.hasChildNodes) { node = node.lastChildNode; result = FilterNode(self.filter, self.whatToShow, node); } - if (result) { + if (result == HTMLNodeFilterAccept) { _currentNode = node; return node; } @@ -179,7 +182,7 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) } node = node.parentNode; - if (FilterNode(self.filter, self.whatToShow, node)) { + if (FilterNode(self.filter, self.whatToShow, node) == HTMLNodeFilterAccept) { _currentNode = node; return node; } @@ -192,13 +195,13 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) { HTMLNode *node = _currentNode; - BOOL result = YES; + HTMLNodeFilterValue result = YES; while (YES) { - while (!result && node.hasChildNodes) { + while (result != HTMLNodeFilterReject && node.hasChildNodes) { node = node.firstChiledNode; result = FilterNode(self.filter, self.whatToShow, node); - if (result) { + if (result == HTMLNodeFilterAccept) { _currentNode = node; return node; } @@ -208,7 +211,7 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) while ((nextSibling = FollowingNodeSkippingChildren(node, _root)) != nil) { node = nextSibling; result = FilterNode(self.filter, self.whatToShow, node); - if (result) { + if (result == HTMLNodeFilterAccept) { _currentNode = node; return node; } diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m index a6411f3..118b5c6 100644 --- a/HTMLKitTests/HTMLKitNodeIteratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -310,13 +310,14 @@ { HTMLDocument *document = self.mixedTree; - HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowAll filterBlock:^BOOL(HTMLNode *node) { + HTMLNodeIterator *iterator = [document nodeIteratorWithShowOptions:HTMLNodeFilterShowAll + filterBlock:^HTMLNodeFilterValue(HTMLNode *node) { if (node.nodeType == HTMLNodeComment) { if ([[(HTMLComment *)node data] rangeOfString:@"second"].location != NSNotFound) { - return YES; + return HTMLNodeFilterAccept; } } - return NO; + return HTMLNodeFilterSkip; }]; NSArray *result = iterator.allObjects; From fd516c67ef5c89938146d20662d52a14af27bebd Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Fri, 5 Jun 2015 23:01:55 +0200 Subject: [PATCH 49/55] Change Node Filter's show options enum to NS_OPTIONS --- HTMLKit/HTMLNodeFilter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h index 9df0e74..a32a2ea 100644 --- a/HTMLKit/HTMLNodeFilter.h +++ b/HTMLKit/HTMLNodeFilter.h @@ -15,7 +15,7 @@ typedef NS_ENUM(unsigned short, HTMLNodeFilterValue) HTMLNodeFilterSkip = 3 }; -typedef NS_ENUM(unsigned long, HTMLNodeFilterShowOptions) +typedef NS_OPTIONS(unsigned long, HTMLNodeFilterShowOptions) { HTMLNodeFilterShowAll = 0xFFFFFFFF, HTMLNodeFilterShowElement = 0x1, From a39890e6e933003b0efd856cb9269ed2f709fc51 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Sat, 6 Jun 2015 18:58:45 +0200 Subject: [PATCH 50/55] Add documentElement property for the HTML Document The documentElement is the <html> element --- HTMLKit/HTMLDocument.h | 2 ++ HTMLKit/HTMLDocument.m | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/HTMLKit/HTMLDocument.h b/HTMLKit/HTMLDocument.h index 222ea81..f8a0485 100644 --- a/HTMLKit/HTMLDocument.h +++ b/HTMLKit/HTMLDocument.h @@ -29,6 +29,8 @@ typedef NS_ENUM(short, HTMLDocumentReadyState) @property (nonatomic, strong) HTMLElement *rootElement; +@property (nonatomic, strong) HTMLElement *documentElement; + @property (nonatomic, strong) HTMLElement *head; @property (nonatomic, strong) HTMLElement *body; diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index a4990dc..86d36c3 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -90,6 +90,21 @@ [self replaceChildNode:self.rootElement withNode:rootElement]; } +- (HTMLElement *)documentElement +{ + for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { + if ([node.asElement.tagName isEqualToString:@"html"]) { + return node.asElement; + } + } + return nil; +} + +- (void)setDocumentElement:(HTMLElement *)documentElement +{ + [self replaceChildNode:self.documentElement withNode:documentElement]; +} + - (HTMLElement *)head { for (HTMLNode *node in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { From 5e482788cebaf67cb5ca869167a7de4556da9086 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Sat, 6 Jun 2015 18:59:06 +0200 Subject: [PATCH 51/55] Add Tree Walker's initializer methods --- HTMLKit/HTMLTreeWalker.h | 7 +++++++ HTMLKit/HTMLTreeWalker.m | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/HTMLKit/HTMLTreeWalker.h b/HTMLKit/HTMLTreeWalker.h index 8b05394..fff2328 100644 --- a/HTMLKit/HTMLTreeWalker.h +++ b/HTMLKit/HTMLTreeWalker.h @@ -18,6 +18,13 @@ @property (nonatomic, strong, readonly) id<HTMLNodeFilter> filter; @property (nonatomic, strong) HTMLNode *currentNode; +- (instancetype)initWithNode:(HTMLNode *)node; +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id<HTMLNodeFilter>)filter; +- (instancetype)initWithNode:(HTMLNode *)node + showOptions:(HTMLNodeFilterShowOptions)showOptions + filter:(id<HTMLNodeFilter>)filter; + - (HTMLNode *)parentNode; - (HTMLNode *)firstChild; - (HTMLNode *)lastChild; diff --git a/HTMLKit/HTMLTreeWalker.m b/HTMLKit/HTMLTreeWalker.m index b76c119..0d27f0e 100644 --- a/HTMLKit/HTMLTreeWalker.m +++ b/HTMLKit/HTMLTreeWalker.m @@ -27,6 +27,17 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) #pragma mark - Lifecycle +- (instancetype)initWithNode:(HTMLNode *)node +{ + return [self initWithNode:node filter:nil]; +} + +- (instancetype)initWithNode:(HTMLNode *)node + filter:(id<HTMLNodeFilter>)filter +{ + return [self initWithNode:node showOptions:HTMLNodeFilterShowAll filter:filter]; +} + - (instancetype)initWithNode:(HTMLNode *)node showOptions:(HTMLNodeFilterShowOptions)showOptions filter:(id<HTMLNodeFilter>)filter From 7f25aacaf1b5e5294e5f75dfabcabe9247fbe3e9 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Sat, 6 Jun 2015 19:00:02 +0200 Subject: [PATCH 52/55] Add test cases for the Tree Walker class --- HTMLKit.xcodeproj/project.pbxproj | 6 + HTMLKit/HTMLTreeWalker.m | 6 +- HTMLKitTests/HTMLTreeWalkerTests.m | 503 +++++++++++++++++++++++++++++ 3 files changed, 512 insertions(+), 3 deletions(-) create mode 100644 HTMLKitTests/HTMLTreeWalkerTests.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index ff7ddb5..1ea6aea 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -69,6 +69,8 @@ 624717181B22333200C11912 /* HTMLNodeTraversal.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717161B22333200C11912 /* HTMLNodeTraversal.h */; }; 624717191B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; }; 6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717171B22333200C11912 /* HTMLNodeTraversal.m */; }; + 6247171C1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */; }; + 6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */; }; 624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; 624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */; }; 624717BC1B22009200B38302 /* HTMLTreeWalker.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717BA1B22009200B38302 /* HTMLTreeWalker.h */; }; @@ -221,6 +223,7 @@ 624493AB19CD0CBE00BCDDF4 /* HTMLToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLToken.m; sourceTree = "<group>"; }; 624717161B22333200C11912 /* HTMLNodeTraversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLNodeTraversal.h; sourceTree = "<group>"; }; 624717171B22333200C11912 /* HTMLNodeTraversal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeTraversal.m; sourceTree = "<group>"; }; + 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalkerTests.m; sourceTree = "<group>"; }; 624717B71B21FE5400B38302 /* HTMLNodeFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNodeFilter.m; sourceTree = "<group>"; }; 624717BA1B22009200B38302 /* HTMLTreeWalker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTreeWalker.h; sourceTree = "<group>"; }; 624717BB1B22009200B38302 /* HTMLTreeWalker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTreeWalker.m; sourceTree = "<group>"; }; @@ -379,6 +382,7 @@ isa = PBXGroup; children = ( 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */, + 6247171B1B2240B800C11912 /* HTMLTreeWalkerTests.m */, 624FC3791AE591D80015DDF9 /* HTMLKitNodesTests.m */, 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */, ); @@ -794,6 +798,7 @@ files = ( 6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, 623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, + 6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */, 62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */, 624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, 6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, @@ -849,6 +854,7 @@ files = ( 6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */, 623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */, + 6247171C1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */, 62EC7AE61AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */, 624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */, 6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */, diff --git a/HTMLKit/HTMLTreeWalker.m b/HTMLKit/HTMLTreeWalker.m index 0d27f0e..7224425 100644 --- a/HTMLKit/HTMLTreeWalker.m +++ b/HTMLKit/HTMLTreeWalker.m @@ -93,7 +93,7 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) HTMLNode *sibling = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.nextSibling : node.previousSibling; if (sibling != nil) { node = sibling; - continue; + break; } HTMLNode *parent = node.parentNode; @@ -135,8 +135,8 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) return node; } - HTMLNode *sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChiledNode : node.lastChildNode; - if (sibling == nil) { + sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChiledNode : node.lastChildNode; + if (result == HTMLNodeFilterReject || sibling == nil) { sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling; } } diff --git a/HTMLKitTests/HTMLTreeWalkerTests.m b/HTMLKitTests/HTMLTreeWalkerTests.m new file mode 100644 index 0000000..256ea32 --- /dev/null +++ b/HTMLKitTests/HTMLTreeWalkerTests.m @@ -0,0 +1,503 @@ + // + // HTMLTreeWalkerTests.m + // HTMLKit + // + // Created by Iska on 05/06/15. + // Copyright (c) 2015 BrainCookie. All rights reserved. + // + +#import <XCTest/XCTest.h> +#import "HTMLTreeWalker.h" +#import "HTMLDOM.h" + +@interface HTMLTreeWalkerTests : XCTestCase + +@end + +@implementation HTMLTreeWalkerTests + +#pragma mark - Asserts + +#define AssertElementWithId(input, id) \ + do { \ + HTMLNode *node = input;\ + XCTAssertEqual(node.nodeType, HTMLNodeElement);\ + XCTAssertEqualObjects(node.asElement[@"id"], id);\ + } while(0) + +#define AssertTextWithValue(input, value) \ + do { \ + HTMLNode *node = input;\ + XCTAssertEqual(node.nodeType, HTMLNodeText);\ + XCTAssertEqualObjects(node.textContent, value);\ + } while(0) + +#define AssertCommentWithValue(input, value) \ + do { \ + HTMLNode *node = input;\ + XCTAssertEqual(node.nodeType, HTMLNodeComment);\ + XCTAssertEqualObjects(node.textContent, value);\ + } while(0) + +#pragma mark - Tests + +- (void)testTreeWalkerInit +{ + HTMLNode *root = self.basicWalkingDOM; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root]; + + XCTAssertNotNil(walker); + XCTAssertNotNil(walker.root); + XCTAssertNotNil(walker.currentNode); + XCTAssertNil(walker.filter); + XCTAssertEqual(walker.whatToShow, HTMLNodeFilterShowAll); + + XCTAssertEqualObjects(walker.root, root); + XCTAssertEqualObjects(walker.root, walker.currentNode); +} + +#pragma mark - Basic Walking + +- (HTMLNode *)basicWalkingDOM +{ + // Tree structure: + // #a + // | + // +----+----+ + // | | + // "b" #c + // | + // +----+----+ + // | | + // #d <!--j--> + // | + // +----+----+ + // | | | + // "e" #f "i" + // | + // +--+--+ + // | | + // "g" <!--h--> + + HTMLElement *div = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"a"}]; + + [div appendNode:[[HTMLText alloc] initWithData:@"b"]]; + + HTMLElement *c = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"c"}]; + [div appendNode:c]; + + HTMLElement *d = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"d"}]; + [c appendNode:d]; + [c appendNode:[[HTMLComment alloc] initWithData:@"j"]]; + + [d appendNode:[[HTMLText alloc] initWithData:@"e"]]; + + HTMLElement *f = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"f"}]; + [d appendNode:f]; + [d appendNode:[[HTMLText alloc] initWithData:@"i"]]; + + [f appendNode:[[HTMLText alloc] initWithData:@"g"]]; + [f appendNode:[[HTMLComment alloc] initWithData:@"h"]]; + + return div; +} + +- (void)testBasicWalking +{ + HTMLNode *root = self.basicWalkingDOM; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root]; + + HTMLNode *f = root.lastChildNode.firstChiledNode.childNodes[1]; + + AssertElementWithId(walker.currentNode, @"a"); + XCTAssertNil(walker.parentNode); + AssertElementWithId(walker.currentNode, @"a"); + + AssertTextWithValue(walker.firstChild, @"b"); + AssertTextWithValue(walker.currentNode, @"b"); + + AssertElementWithId(walker.nextSibling, @"c"); + AssertElementWithId(walker.currentNode, @"c"); + + AssertCommentWithValue(walker.lastChild, @"j"); + AssertCommentWithValue(walker.currentNode, @"j"); + + AssertElementWithId(walker.previousSibling, @"d"); + AssertElementWithId(walker.currentNode, @"d"); + + AssertTextWithValue(walker.nextNode, @"e"); + AssertTextWithValue(walker.currentNode, @"e"); + + AssertElementWithId(walker.parentNode, @"d"); + AssertElementWithId(walker.currentNode, @"d"); + + AssertElementWithId(walker.previousNode, @"c"); + AssertElementWithId(walker.currentNode, @"c"); + + XCTAssertNil(walker.nextSibling); + AssertElementWithId(walker.currentNode, @"c"); + + walker.currentNode = f; + XCTAssertEqualObjects(walker.currentNode, f); +} + +#pragma mark - Current Node + +- (HTMLDocument *)currentNodeDOM +{ + HTMLDocument *document = [HTMLDocument documentWithString: + @"<div id='first'><p><a></a></p></div>" + @"<div id='second'><p><b></b></p</div>"]; + + return document; +} + +- (void)testThatTreeWalkerParentHasNoEffectCurrentNodeWhenParentIsNotUnderRoot +{ + HTMLDocument *document = self.currentNodeDOM; + HTMLNode *first = document.body.firstChiledNode; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first + showOptions:HTMLNodeFilterShowElement + filter:nil]; + + AssertElementWithId(walker.currentNode, @"first"); + XCTAssertNil(walker.parentNode); + AssertElementWithId(walker.currentNode, @"first"); +} + +- (void)testThatSettingCurrentNodeToNodesNotUnderRootIsHandledCorrectly +{ + HTMLDocument *document = self.currentNodeDOM; + HTMLNode *first = document.body.firstChiledNode; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first + showOptions:HTMLNodeFilterShowElement|HTMLNodeFilterShowComment + filter:nil]; + walker.currentNode = document.documentElement; + XCTAssertNil(walker.parentNode); + XCTAssertEqualObjects(walker.currentNode, document.documentElement); + + walker.currentNode = document.documentElement; + XCTAssertEqualObjects(walker.nextNode, document.documentElement.firstChiledNode); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChiledNode); + + walker.currentNode = document.documentElement; + XCTAssertNil(walker.previousNode); + XCTAssertEqualObjects(walker.currentNode, document.documentElement); + + walker.currentNode = document.documentElement; + XCTAssertEqualObjects(walker.firstChild, document.documentElement.firstChiledNode); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChiledNode); + + walker.currentNode = document.documentElement; + XCTAssertEqualObjects(walker.lastChild, document.documentElement.lastChildNode); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.lastChildNode); + + walker.currentNode = document.documentElement; + XCTAssertNil(walker.nextSibling); + XCTAssertEqualObjects(walker.currentNode, document.documentElement); + + walker.currentNode = document.documentElement; + XCTAssertNil(walker.previousSibling); + XCTAssertEqualObjects(walker.currentNode, document.documentElement); +} + +#pragma mark - Filter + +- (HTMLElement *)filterBasicDOM +{ + HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"root"}]; + + HTMLElement *a1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"A1"}]; + HTMLElement *b1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B1"}]; + HTMLElement *b2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B2"}]; + + [root appendNode:a1]; + [a1 appendNode:b1]; + [a1 appendNode:b2]; + + return root; +} + +- (void)testTreeWalkerNilFilter +{ + HTMLElement *root = self.filterBasicDOM; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:nil]; + + AssertElementWithId(walker.currentNode, @"root"); + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.currentNode, @"A1"); + AssertElementWithId(walker.nextNode, @"B1"); + AssertElementWithId(walker.currentNode, @"B1"); +} + +- (void)testTreeWalkerWithFilter +{ + HTMLElement *root = self.filterBasicDOM; + + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B1"]) { + return HTMLNodeFilterSkip; + } + return HTMLNodeFilterAccept; + }]; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.currentNode, @"root"); + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.currentNode, @"A1"); + AssertElementWithId(walker.nextNode, @"B2"); + AssertElementWithId(walker.currentNode, @"B2"); +} + +#pragma mark - Filter Skip + +- (HTMLElement *)filterDOM +{ + HTMLElement *root = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"root"}]; + + HTMLElement *a1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"A1"}]; + HTMLElement *b1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B1"}]; + HTMLElement *b2 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B2"}]; + HTMLElement *b3 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"B3"}]; + HTMLElement *c1 = [[HTMLElement alloc] initWithTagName:@"div" attributes:@{@"id": @"C1"}]; + + [root appendNode:a1]; + [a1 appendNode:b1]; + [a1 appendNode:b2]; + [a1 appendNode:b3]; + [b1 appendNode:c1]; + + return root; +} + +- (id<HTMLNodeFilter>)skipB1Filter +{ + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B1"]) { + return HTMLNodeFilterSkip; + } + return HTMLNodeFilterAccept; + }]; + return filter; +} + +- (id<HTMLNodeFilter>)skipB2Filter +{ + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B2"]) { + return HTMLNodeFilterSkip; + } + return HTMLNodeFilterAccept; + }]; + return filter; +} + +- (id<HTMLNodeFilter>)rejectB1Filter +{ + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B1"]) { + return HTMLNodeFilterReject; + } + return HTMLNodeFilterAccept; + }]; + return filter; +} + +- (id<HTMLNodeFilter>)rejectB2Filter +{ + id<HTMLNodeFilter> filter = [HTMLNodeFilterBlock filterWithBlock:^HTMLNodeFilterValue(HTMLNode *node) { + if ([node.asElement[@"id"] isEqualToString:@"B2"]) { + return HTMLNodeFilterReject; + } + return HTMLNodeFilterAccept; + }]; + return filter; +} + +static HTMLElement * (^ FindElementById)(HTMLNode *, NSString *) = ^ HTMLElement * (HTMLNode *root, NSString *id) { + for (HTMLNode *node in [root nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) { + if ([node.asElement[@"id"] isEqualToString:id]) { + return node.asElement; + } + } + return nil; +}; + +- (void)testThatFilterSkipsNextNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.nextNode, @"A1"); + AssertElementWithId(walker.nextNode, @"C1"); + AssertElementWithId(walker.nextNode, @"B2"); + AssertElementWithId(walker.nextNode, @"B3"); +} + +- (void)testThatFilterSkipsFirstChild +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.firstChild, @"C1"); +} + +- (void)testThatFilterSkipsNextSibling +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB2Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.firstChild, @"B1"); + AssertElementWithId(walker.nextSibling, @"B3"); +} + +- (void)testThatFilterSkipsParentNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"C1"); + AssertElementWithId(walker.parentNode, @"A1"); +} + +- (void)testThatFilterSkipsPreviousSibling +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB2Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"B3"); + AssertElementWithId(walker.previousSibling, @"B1"); +} + +- (void)testThatFilterSkipsPreviousNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.skipB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"B3"); + AssertElementWithId(walker.previousNode, @"B2"); + AssertElementWithId(walker.previousNode, @"C1"); + AssertElementWithId(walker.previousNode, @"A1"); +} + +#pragma mark - Filter Reject + +- (void)testThatFilterRejectsNextNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.nextNode, @"A1"); + AssertElementWithId(walker.nextNode, @"B2"); + AssertElementWithId(walker.nextNode, @"B3"); +} + +- (void)testThatFilterRejectsFirstChild +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.firstChild, @"B2"); +} + +- (void)testThatFilterRejectsNextSibling +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB2Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + AssertElementWithId(walker.firstChild, @"A1"); + AssertElementWithId(walker.firstChild, @"B1"); + AssertElementWithId(walker.nextSibling, @"B3"); +} + +- (void)testThatFilterRejectsParentNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"C1"); + AssertElementWithId(walker.parentNode, @"A1"); +} + +- (void)testThatFilterRejectsPreviousSibling +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB2Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"B3"); + AssertElementWithId(walker.previousSibling, @"B1"); +} + +- (void)testThatFilterRejectsPreviousNode +{ + HTMLElement *root = self.filterDOM; + id<HTMLNodeFilter> filter = self.rejectB1Filter; + + HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root + showOptions:HTMLNodeFilterShowElement + filter:filter]; + + walker.currentNode = FindElementById(root, @"B3"); + AssertElementWithId(walker.previousNode, @"B2"); + AssertElementWithId(walker.previousNode, @"A1"); +} + + +@end From df5387088081dff5b52f12cf16261268e4242f16 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Sat, 6 Jun 2015 19:04:03 +0200 Subject: [PATCH 53/55] Drop the "Node" suffix from HTML Node's first child & last child properties https://dom.spec.whatwg.org/#interface-node --- HTMLKit/HTMLDocument.m | 2 +- HTMLKit/HTMLElement.m | 4 +-- HTMLKit/HTMLNode.h | 4 +-- HTMLKit/HTMLNode.m | 8 ++--- HTMLKit/HTMLNodeIterator.m | 6 ++-- HTMLKit/HTMLNodeTraversal.m | 8 ++--- HTMLKit/HTMLParser.m | 4 +-- HTMLKit/HTMLTreeWalker.m | 10 +++---- HTMLKitTests/HTMLKitNodeIteratorTests.m | 24 +++++++-------- HTMLKitTests/HTMLKitNodesTests.m | 40 ++++++++++++------------- HTMLKitTests/HTMLTreeWalkerTests.m | 18 +++++------ 11 files changed, 64 insertions(+), 64 deletions(-) diff --git a/HTMLKit/HTMLDocument.m b/HTMLKit/HTMLDocument.m index 86d36c3..09baa88 100644 --- a/HTMLKit/HTMLDocument.m +++ b/HTMLKit/HTMLDocument.m @@ -77,7 +77,7 @@ - (HTMLElement *)rootElement { - for (HTMLNode *node = self.firstChiledNode; node; node = node.nextSibling) { + for (HTMLNode *node = self.firstChild; node; node = node.nextSibling) { if (node.nodeType == HTMLNodeElement) { return node.asElement; } diff --git a/HTMLKit/HTMLElement.m b/HTMLKit/HTMLElement.m index 1c018da..6083537 100644 --- a/HTMLKit/HTMLElement.m +++ b/HTMLKit/HTMLElement.m @@ -146,8 +146,8 @@ return result; } - if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChiledNode.nodeType == HTMLNodeText) { - HTMLText *textNode = (HTMLText *)self.firstChiledNode; + if ([self.tagName isEqualToAny:@"pre", @"textarea", @"listing", nil] && self.firstChild.nodeType == HTMLNodeText) { + HTMLText *textNode = (HTMLText *)self.firstChild; if ([textNode.data hasPrefix:@"\n"]) { [result appendString:@"\n"]; } diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h index a488dcd..eb07dbc 100644 --- a/HTMLKit/HTMLNode.h +++ b/HTMLKit/HTMLNode.h @@ -53,9 +53,9 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition) @property (nonatomic, strong, readonly) NSOrderedSet *childNodes; -@property (nonatomic, strong, readonly) HTMLNode *firstChiledNode; +@property (nonatomic, strong, readonly) HTMLNode *firstChild; -@property (nonatomic, strong, readonly) HTMLNode *lastChildNode; +@property (nonatomic, strong, readonly) HTMLNode *lastChild; @property (nonatomic, strong, readonly) HTMLNode *previousSibling; diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m index f6cedbc..fdff072 100644 --- a/HTMLKit/HTMLNode.m +++ b/HTMLKit/HTMLNode.m @@ -70,12 +70,12 @@ return _parentNode.nodeType == HTMLNodeElement ? (HTMLElement *)_parentNode : nil; } -- (HTMLNode *)firstChiledNode +- (HTMLNode *)firstChild { return self.childNodes.firstObject; } -- (HTMLNode *)lastChildNode +- (HTMLNode *)lastChild { return self.childNodes.lastObject; } @@ -147,13 +147,13 @@ - (HTMLNode *)prependNode:(HTMLNode *)node { - return [self insertNode:node beforeChildNode:self.firstChiledNode]; + return [self insertNode:node beforeChildNode:self.firstChild]; } - (void)prependNodes:(NSArray *)nodes { for (id node in nodes.reverseObjectEnumerator) { - [self insertNode:node beforeChildNode:self.firstChiledNode]; + [self insertNode:node beforeChildNode:self.firstChild]; } } diff --git a/HTMLKit/HTMLNodeIterator.m b/HTMLKit/HTMLNodeIterator.m index c5e85d1..6bfb9f4 100644 --- a/HTMLKit/HTMLNodeIterator.m +++ b/HTMLKit/HTMLNodeIterator.m @@ -89,7 +89,7 @@ typedef NS_ENUM(short, TraverseDirection) } if (_pointerBeforeReferenceNode) { - HTMLNode *nextSibling = oldPreviousSibling != nil ? oldPreviousSibling.nextSibling : oldParent.firstChiledNode; + HTMLNode *nextSibling = oldPreviousSibling != nil ? oldPreviousSibling.nextSibling : oldParent.firstChild; if (nextSibling != nil) { _referenceNode = nextSibling; return; @@ -105,8 +105,8 @@ typedef NS_ENUM(short, TraverseDirection) } HTMLNode * (^ lastInclusiveDescendant) (HTMLNode *) = ^ HTMLNode * (HTMLNode *node) { - while (node.lastChildNode) { - node = node.lastChildNode; + while (node.lastChild) { + node = node.lastChild; } return node; }; diff --git a/HTMLKit/HTMLNodeTraversal.m b/HTMLKit/HTMLNodeTraversal.m index 1c646df..dd17b2a 100644 --- a/HTMLKit/HTMLNodeTraversal.m +++ b/HTMLKit/HTMLNodeTraversal.m @@ -14,8 +14,8 @@ HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root) { HTMLNode *previous = node.previousSibling; if (previous != nil) { - while (previous.lastChildNode != nil) { - previous = previous.lastChildNode; + while (previous.lastChild != nil) { + previous = previous.lastChild; } return previous; } @@ -29,8 +29,8 @@ HTMLNode * PrecedingNode(HTMLNode *node, HTMLNode *root) HTMLNode * FollowingNode(HTMLNode *node, HTMLNode *root) { - if (node.firstChiledNode != nil) { - return node.firstChiledNode; + if (node.firstChild != nil) { + return node.firstChild; } do { diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m index d561e59..6f7bb46 100644 --- a/HTMLKit/HTMLParser.m +++ b/HTMLKit/HTMLParser.m @@ -455,8 +455,8 @@ if (adjustedInsertionLocation.nodeType != HTMLNodeDocument) { if (child != nil && child.previousSibling.nodeType == HTMLNodeText) { [(HTMLText *)child.previousSibling appendString:data]; - } else if (adjustedInsertionLocation.lastChildNode.nodeType == HTMLNodeText) { - [(HTMLText *)adjustedInsertionLocation.lastChildNode appendString:data]; + } else if (adjustedInsertionLocation.lastChild.nodeType == HTMLNodeText) { + [(HTMLText *)adjustedInsertionLocation.lastChild appendString:data]; } else { HTMLText *text = [[HTMLText alloc] initWithData:data]; [adjustedInsertionLocation insertNode:text beforeChildNode:child]; diff --git a/HTMLKit/HTMLTreeWalker.m b/HTMLKit/HTMLTreeWalker.m index 7224425..0e21ed0 100644 --- a/HTMLKit/HTMLTreeWalker.m +++ b/HTMLKit/HTMLTreeWalker.m @@ -72,7 +72,7 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) { HTMLNode *node = _currentNode; - node = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode; + node = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChild : node.lastChild; while (node != nil) { HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node); @@ -82,7 +82,7 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) } if (result == HTMLNodeFilterSkip) { - HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChiledNode : node.lastChildNode; + HTMLNode *child = (type == HTMLTreeWalkerChildrenTypeFirst) ? node.firstChild : node.lastChild; if (child != nil) { node = child; continue; @@ -135,7 +135,7 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) return node; } - sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChiledNode : node.lastChildNode; + sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.firstChild : node.lastChild; if (result == HTMLNodeFilterReject || sibling == nil) { sibling = (type == HTMLTreeWalkerSiblingsTypeNext) ? node.nextSibling : node.previousSibling; } @@ -176,7 +176,7 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) HTMLNodeFilterValue result = FilterNode(self.filter, self.whatToShow, node); while (result != HTMLNodeFilterReject && node.hasChildNodes) { - node = node.lastChildNode; + node = node.lastChild; result = FilterNode(self.filter, self.whatToShow, node); } @@ -210,7 +210,7 @@ typedef NS_ENUM(short, HTMLTreeWalkerSiblingsType) while (YES) { while (result != HTMLNodeFilterReject && node.hasChildNodes) { - node = node.firstChiledNode; + node = node.firstChild; result = FilterNode(self.filter, self.whatToShow, node); if (result == HTMLNodeFilterAccept) { _currentNode = node; diff --git a/HTMLKitTests/HTMLKitNodeIteratorTests.m b/HTMLKitTests/HTMLKitNodeIteratorTests.m index 118b5c6..7ff6823 100644 --- a/HTMLKitTests/HTMLKitNodeIteratorTests.m +++ b/HTMLKitTests/HTMLKitNodeIteratorTests.m @@ -367,8 +367,8 @@ static void (^ IterateUpToNode)(HTMLNodeIterator *, HTMLNode *) = ^ (HTMLNodeIte }; static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) { - while (node.lastChildNode) { - node = node.lastChildNode; + while (node.lastChild) { + node = node.lastChild; } return node; }; @@ -376,7 +376,7 @@ static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) - (void)testThatRemovingRootNodeShouldNotAffectIterator { HTMLDocument *document = self.document; - HTMLNode *node = document.body.firstChiledNode; // <span> + HTMLNode *node = document.body.firstChild; // <span> HTMLNodeIterator *iterator = node.nodeIterator; @@ -401,7 +401,7 @@ static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) RemoveThenInsertNode(iterator.root.childNodes[1]); // Remove <p> XCTAssertEqualObjects(iterator.root, body); - XCTAssertEqualObjects(iterator.referenceNode, body.firstChiledNode); + XCTAssertEqualObjects(iterator.referenceNode, body.firstChild); XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); } @@ -420,7 +420,7 @@ static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); HTMLNode *next = iterator.nextNode; // "Hello" - XCTAssertEqualObjects(next, iterator.root.firstChiledNode); + XCTAssertEqualObjects(next, iterator.root.firstChild); } - (void)testThatRemovingReferenceNodeShouldUpdateIterator_NonNilOldPreviousSibling_NotBeforeReference @@ -434,7 +434,7 @@ static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) IterateUpToNode(iterator, node); // Reference node: <p>, pointer-before-reference: NO RemoveThenInsertNode(node); // Remove <p> with old previos sibling being <span> - XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChiledNode)); + XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChild)); XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); } @@ -450,7 +450,7 @@ static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) [iterator previousNode]; // pointer-before-reference: YES RemoveThenInsertNode(node); // Remove <p> with old previos sibling being <span> - XCTAssertEqualObjects(iterator.referenceNode, body.firstChiledNode); + XCTAssertEqualObjects(iterator.referenceNode, body.firstChild); XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); } @@ -502,7 +502,7 @@ static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) [iterator nextNode]; // Reference node: "This is an " RemoveThenInsertNode(parent); - XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChiledNode)); + XCTAssertEqualObjects(iterator.referenceNode, LastDescendant(body.firstChild)); XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); } @@ -519,7 +519,7 @@ static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) [iterator previousNode]; // pointer-before-reference: YES RemoveThenInsertNode(parent); - XCTAssertEqualObjects(iterator.referenceNode, body.firstChiledNode); + XCTAssertEqualObjects(iterator.referenceNode, body.firstChild); XCTAssertEqual(iterator.pointerBeforeReferenceNode, YES); } @@ -555,13 +555,13 @@ static HTMLNode * (^ LastDescendant)(HTMLNode *) = ^ HTMLNode * (HTMLNode *node) body.innerHTML = @"<div><p><a></a></p></div><div></div>"; - HTMLNodeIterator *iterator = body.firstChiledNode.nodeIterator; + HTMLNodeIterator *iterator = body.firstChild.nodeIterator; - IterateUpToNode(iterator, LastDescendant(body.firstChiledNode)); // Referecne node: <a> + IterateUpToNode(iterator, LastDescendant(body.firstChild)); // Referecne node: <a> HTMLNode *node = [iterator previousNode]; // pointer-before-reference: YES RemoveThenInsertNode(node); - XCTAssertEqualObjects(iterator.referenceNode, iterator.root.firstChiledNode); + XCTAssertEqualObjects(iterator.referenceNode, iterator.root.firstChild); XCTAssertEqual(iterator.pointerBeforeReferenceNode, NO); } diff --git a/HTMLKitTests/HTMLKitNodesTests.m b/HTMLKitTests/HTMLKitNodesTests.m index 524a25c..87f37f9 100644 --- a/HTMLKitTests/HTMLKitNodesTests.m +++ b/HTMLKitTests/HTMLKitNodesTests.m @@ -37,10 +37,10 @@ XCTAssertNil(node.ownerDocument); XCTAssertNil(node.parentNode); XCTAssertNil(node.parentElement); - XCTAssertNil(node.firstChiledNode); - XCTAssertNil(node.lastChildNode); + XCTAssertNil(node.firstChild); + XCTAssertNil(node.lastChild); XCTAssertNil(node.previousSibling); - XCTAssertNil(node.lastChildNode); + XCTAssertNil(node.lastChild); } - (void)testAppendNode @@ -50,7 +50,7 @@ [element appendNode:comment]; XCTAssertEqual(element.childNodesCount, 1); - XCTAssertEqual(element.firstChiledNode, comment); + XCTAssertEqual(element.firstChild, comment); HTMLElement *firstElement = [HTMLElement new]; HTMLElement *secondElement = [HTMLElement new]; @@ -59,8 +59,8 @@ [element appendNodes:nodes]; XCTAssertEqual(element.childNodesCount, 3); - XCTAssertEqual(element.firstChiledNode, comment); - XCTAssertEqual(element.lastChildNode, secondElement); + XCTAssertEqual(element.firstChild, comment); + XCTAssertEqual(element.lastChild, secondElement); } - (void)testPrependNode @@ -73,7 +73,7 @@ [element prependNode:comment]; XCTAssertEqual(element.childNodesCount, 2); - XCTAssertEqual(element.firstChiledNode, comment); + XCTAssertEqual(element.firstChild, comment); HTMLElement *firstElement = [HTMLElement new]; HTMLElement *secondElement = [HTMLElement new]; @@ -82,8 +82,8 @@ [element prependNodes:nodes]; XCTAssertEqual(element.childNodesCount, 4); - XCTAssertEqual(element.firstChiledNode, firstElement); - XCTAssertEqual(element.lastChildNode, text); + XCTAssertEqual(element.firstChild, firstElement); + XCTAssertEqual(element.lastChild, text); } - (void)testAppendDocumentFragment @@ -104,10 +104,10 @@ XCTAssertEqualObjects(firstChild.parentNode, element); XCTAssertEqualObjects(secondChild.parentNode, element); - XCTAssertEqualObjects(element.firstChiledNode, comment); - XCTAssertEqualObjects(element.firstChiledNode.nextSibling, firstChild); - XCTAssertEqualObjects(element.lastChildNode.previousSibling, firstChild); - XCTAssertEqualObjects(element.lastChildNode, secondChild); + XCTAssertEqualObjects(element.firstChild, comment); + XCTAssertEqualObjects(element.firstChild.nextSibling, firstChild); + XCTAssertEqualObjects(element.lastChild.previousSibling, firstChild); + XCTAssertEqualObjects(element.lastChild, secondChild); } - (void)testParentNode @@ -170,20 +170,20 @@ HTMLNode *firstChild = [[HTMLNode alloc] initWithName:@"first" type:HTMLNodeElement]; [node appendNode:firstChild]; - XCTAssertEqualObjects(node.firstChiledNode, firstChild); - XCTAssertEqualObjects(node.lastChildNode, firstChild); + XCTAssertEqualObjects(node.firstChild, firstChild); + XCTAssertEqualObjects(node.lastChild, firstChild); HTMLNode *secondChild = [[HTMLNode alloc] initWithName:@"second" type:HTMLNodeElement]; [node appendNode:secondChild]; - XCTAssertEqualObjects(node.firstChiledNode, firstChild); - XCTAssertEqualObjects(node.lastChildNode, secondChild); + XCTAssertEqualObjects(node.firstChild, firstChild); + XCTAssertEqualObjects(node.lastChild, secondChild); HTMLNode *thirdChild = [[HTMLNode alloc] initWithName:@"third" type:HTMLNodeElement]; [node appendNode:thirdChild]; - XCTAssertEqualObjects(node.firstChiledNode, firstChild); - XCTAssertEqualObjects(node.lastChildNode, thirdChild); + XCTAssertEqualObjects(node.firstChild, firstChild); + XCTAssertEqualObjects(node.lastChild, thirdChild); } - (void)testNextAndPreviousSiblingNodes @@ -478,7 +478,7 @@ XCTAssertEqual(element.childNodesCount, 2); - XCTAssertEqualObjects(element.firstChiledNode.asElement.tagName, @"p"); + XCTAssertEqualObjects(element.firstChild.asElement.tagName, @"p"); XCTAssertEqualObjects([element childNodeAtIndex:1].asElement.tagName, @"p"); } diff --git a/HTMLKitTests/HTMLTreeWalkerTests.m b/HTMLKitTests/HTMLTreeWalkerTests.m index 256ea32..6aeeaf2 100644 --- a/HTMLKitTests/HTMLTreeWalkerTests.m +++ b/HTMLKitTests/HTMLTreeWalkerTests.m @@ -109,7 +109,7 @@ HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:root]; - HTMLNode *f = root.lastChildNode.firstChiledNode.childNodes[1]; + HTMLNode *f = root.lastChild.firstChild.childNodes[1]; AssertElementWithId(walker.currentNode, @"a"); XCTAssertNil(walker.parentNode); @@ -157,7 +157,7 @@ - (void)testThatTreeWalkerParentHasNoEffectCurrentNodeWhenParentIsNotUnderRoot { HTMLDocument *document = self.currentNodeDOM; - HTMLNode *first = document.body.firstChiledNode; + HTMLNode *first = document.body.firstChild; HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first showOptions:HTMLNodeFilterShowElement @@ -171,7 +171,7 @@ - (void)testThatSettingCurrentNodeToNodesNotUnderRootIsHandledCorrectly { HTMLDocument *document = self.currentNodeDOM; - HTMLNode *first = document.body.firstChiledNode; + HTMLNode *first = document.body.firstChild; HTMLTreeWalker *walker = [[HTMLTreeWalker alloc] initWithNode:first showOptions:HTMLNodeFilterShowElement|HTMLNodeFilterShowComment @@ -181,20 +181,20 @@ XCTAssertEqualObjects(walker.currentNode, document.documentElement); walker.currentNode = document.documentElement; - XCTAssertEqualObjects(walker.nextNode, document.documentElement.firstChiledNode); - XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChiledNode); + XCTAssertEqualObjects(walker.nextNode, document.documentElement.firstChild); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChild); walker.currentNode = document.documentElement; XCTAssertNil(walker.previousNode); XCTAssertEqualObjects(walker.currentNode, document.documentElement); walker.currentNode = document.documentElement; - XCTAssertEqualObjects(walker.firstChild, document.documentElement.firstChiledNode); - XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChiledNode); + XCTAssertEqualObjects(walker.firstChild, document.documentElement.firstChild); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.firstChild); walker.currentNode = document.documentElement; - XCTAssertEqualObjects(walker.lastChild, document.documentElement.lastChildNode); - XCTAssertEqualObjects(walker.currentNode, document.documentElement.lastChildNode); + XCTAssertEqualObjects(walker.lastChild, document.documentElement.lastChild); + XCTAssertEqualObjects(walker.currentNode, document.documentElement.lastChild); walker.currentNode = document.documentElement; XCTAssertNil(walker.nextSibling); From a7caa347624f5beba255bda9d1a5a139b9e11355 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Sat, 6 Jun 2015 20:10:28 +0200 Subject: [PATCH 54/55] Disable HTML DOM checks in parser's performance test The baseline was set before the DOM validations were implemented. --- HTMLKitTests/HTMLKitParserPerformance.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HTMLKitTests/HTMLKitParserPerformance.m b/HTMLKitTests/HTMLKitParserPerformance.m index f4839d0..005282f 100644 --- a/HTMLKitTests/HTMLKitParserPerformance.m +++ b/HTMLKitTests/HTMLKitParserPerformance.m @@ -15,6 +15,8 @@ @implementation HTMLKitParserPerformance +#define HTMLKIT_NO_DOM_CHECKS + - (void)testParserPerformance { NSString *path = [[NSBundle bundleForClass:self.class] resourcePath]; @@ -28,4 +30,6 @@ }]; } +#undef HTMLKIT_NO_DOM_CHECKS + @end From 045e4db9d1e3d80d0ae16578cb56a57a98ae8663 Mon Sep 17 00:00:00 2001 From: iska <iska.gp@gmail.com> Date: Sat, 6 Jun 2015 20:11:49 +0200 Subject: [PATCH 55/55] Add node iterator, tree walker & node filter imports to the HTMLDOM header --- HTMLKit/HTMLDOM.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/HTMLKit/HTMLDOM.h b/HTMLKit/HTMLDOM.h index 4e9d5ce..c570ae0 100644 --- a/HTMLKit/HTMLDOM.h +++ b/HTMLKit/HTMLDOM.h @@ -14,3 +14,6 @@ #import "HTMLComment.h" #import "HTMLText.h" #import "HTMLTemplate.h" +#import "HTMLNodeIterator.h" +#import "HTMLTreeWalker.h" +#import "HTMLNodeFilter.h"