diff --git a/.gitignore b/.gitignore
index bd9f093..b0f2379 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,12 @@
-# OS X
-.DS_Store
-
# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## Build generated
build/
+DerivedData
+
+## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
@@ -12,13 +16,28 @@ build/
*.perspectivev3
!default.perspectivev3
xcuserdata
+
+## Other
*.xccheckout
-profile
*.moved-aside
-DerivedData
+*.xcuserstate
+*.xcscmblueprint
+
+## Obj-C/Swift specific
*.hmap
*.ipa
# CocoaPods
-Pods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+Pods/
+# Carthage
+#
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+Carthage/Checkouts
+
+Carthage/Build
\ No newline at end of file
diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj
index 1ea6aea..27284ab 100644
--- a/HTMLKit.xcodeproj/project.pbxproj
+++ b/HTMLKit.xcodeproj/project.pbxproj
@@ -7,49 +7,51 @@
objects = {
/* Begin PBXBuildFile section */
- 623406E11ADB04F9004677A3 /* HTMLTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */; };
+ 620C877C1BD44CBE00FB3EEE /* CSSCompoundSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */; };
+ 620C877D1BD44CBE00FB3EEE /* CSSCompoundSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */; };
+ 620C877E1BD4519A00FB3EEE /* CSSPseudoFunctionSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62ACB8DC1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.m */; };
+ 620C877F1BD4519A00FB3EEE /* CSSPseudoFunctionSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62ACB8DC1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.m */; };
+ 620EE94A1BC46F2A0028ED34 /* CSSPseudoClassSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 620EE9481BC46F2A0028ED34 /* CSSPseudoClassSelector.m */; };
+ 620EE94B1BC46F2A0028ED34 /* CSSPseudoClassSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 620EE9481BC46F2A0028ED34 /* CSSPseudoClassSelector.m */; };
+ 62132E591C01F83200084175 /* CSSSelectorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62132E581C01F83200084175 /* CSSSelectorTest.m */; };
+ 62132E5A1C01F83200084175 /* CSSSelectorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62132E581C01F83200084175 /* CSSSelectorTest.m */; };
+ 62132E5C1C021FF200084175 /* css-tests in Resources */ = {isa = PBXBuildFile; fileRef = 62132E5B1C021FF200084175 /* css-tests */; };
+ 62132E5D1C021FF200084175 /* css-tests in Resources */ = {isa = PBXBuildFile; fileRef = 62132E5B1C021FF200084175 /* css-tests */; };
+ 621FBE5A1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */; };
+ 621FBE5B1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */; };
+ 621FBE5D1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */; };
+ 621FBE5E1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */; };
+ 622BF5821BBF2FD700363583 /* CSSSelectorParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 622BF5801BBF2FD700363583 /* CSSSelectorParser.m */; };
+ 622BF5831BBF2FD700363583 /* CSSSelectorParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 622BF5801BBF2FD700363583 /* CSSSelectorParser.m */; };
623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E01ADB04F9004677A3 /* HTMLTemplate.m */; };
623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E01ADB04F9004677A3 /* HTMLTemplate.m */; };
- 623406E61ADB05AD004677A3 /* HTMLDocumentFragment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */; };
623406E71ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */; };
623406E81ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */ = {isa = PBXBuildFile; fileRef = 623406E51ADB05AD004677A3 /* HTMLDocumentFragment.m */; };
- 623424881AB467B200726190 /* HTMLOrderedDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 623424861AB467B200726190 /* HTMLOrderedDictionary.h */; };
623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 623424871AB467B200726190 /* HTMLOrderedDictionary.m */; };
6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 623424871AB467B200726190 /* HTMLOrderedDictionary.m */; };
- 6234584E1A9D2FA4009BD491 /* HTMLNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234584C1A9D2FA4009BD491 /* HTMLNode.h */; };
6234584F1A9D2FA4009BD491 /* HTMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234584D1A9D2FA4009BD491 /* HTMLNode.m */; };
623458501A9D2FA4009BD491 /* HTMLNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234584D1A9D2FA4009BD491 /* HTMLNode.m */; };
- 6234BEEE1AABBF1400DEB15F /* HTMLStackOfOpenElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234BEEC1AABBF1400DEB15F /* HTMLStackOfOpenElements.h */; };
6234BEEF1AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */; };
6234BEF01AABBF1400DEB15F /* HTMLStackOfOpenElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 6234BEED1AABBF1400DEB15F /* HTMLStackOfOpenElements.m */; };
6234CF571ABF892400B6077D /* libHTMLKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DE19C7834000AD0C32 /* libHTMLKit.a */; };
- 6235CE9B1AA509430026937B /* NSString+HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE991AA509430026937B /* NSString+HTMLKit.h */; };
6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */; };
6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */; };
- 6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE9E1AA5170A0026937B /* HTMLMarker.h */; };
6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9F1AA5170A0026937B /* HTMLMarker.m */; };
6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6235CE9F1AA5170A0026937B /* HTMLMarker.m */; };
- 62362A3C1A9FA70400301989 /* HTMLText.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3A1A9FA70400301989 /* HTMLText.h */; };
62362A3D1A9FA70400301989 /* HTMLText.m in Sources */ = {isa = PBXBuildFile; fileRef = 62362A3B1A9FA70400301989 /* HTMLText.m */; };
62362A3E1A9FA70400301989 /* HTMLText.m in Sources */ = {isa = PBXBuildFile; fileRef = 62362A3B1A9FA70400301989 /* HTMLText.m */; };
- 62363C3D1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */; };
62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */; };
62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */ = {isa = PBXBuildFile; fileRef = 62363C3C1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m */; };
6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */; };
6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6236738D1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m */; };
- 6238039F1AB63A8C008A53D0 /* HTMLEOFToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238039D1AB63A8C008A53D0 /* HTMLEOFToken.h */; };
623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238039E1AB63A8C008A53D0 /* HTMLEOFToken.m */; };
623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 6238039E1AB63A8C008A53D0 /* HTMLEOFToken.m */; };
- 623857911A9E772B003A45D9 /* HTMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238578F1A9E772B003A45D9 /* HTMLDocument.h */; };
623857921A9E772B003A45D9 /* HTMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857901A9E772B003A45D9 /* HTMLDocument.m */; };
623857931A9E772B003A45D9 /* HTMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857901A9E772B003A45D9 /* HTMLDocument.m */; };
- 623857961A9E8606003A45D9 /* HTMLDocumentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857941A9E8606003A45D9 /* HTMLDocumentType.h */; };
623857971A9E8606003A45D9 /* HTMLDocumentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857951A9E8606003A45D9 /* HTMLDocumentType.m */; };
623857981A9E8606003A45D9 /* HTMLDocumentType.m in Sources */ = {isa = PBXBuildFile; fileRef = 623857951A9E8606003A45D9 /* HTMLDocumentType.m */; };
- 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 /* 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 */; };
623916C71AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 623916C61AC7209E0066B4FE /* HTMLKitNodeIteratorTests.m */; };
@@ -60,33 +62,38 @@
6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6239755D1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m */; };
623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; };
623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */ = {isa = PBXBuildFile; fileRef = 623CAF9C1AD88BEA00E34C32 /* HTMLKitParserPerformance.m */; };
- 624493A619CCC54100BCDDF4 /* HTMLTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */; };
624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */; };
624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 624493A519CCC54100BCDDF4 /* HTMLTokenizer.m */; };
- 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 */; };
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 */; };
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 */; };
- 6247A9471B152F8C00CCF25C /* HTMLNodeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */; };
- 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */; };
+ 624AB3171B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */; };
+ 624AB3181B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */; };
+ 624AB31A1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */; };
+ 624AB31B1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */; };
624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; };
624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624AC90019FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m */; };
624AC90519FBFE9A00BD3C4A /* html5lib-tests in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90419FBFE8A00BD3C4A /* html5lib-tests */; };
624AC90619FBFEA000BD3C4A /* html5lib-tests in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90419FBFE8A00BD3C4A /* html5lib-tests */; };
624AC90E19FC702E00BD3C4A /* HTML Standard.html in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90D19FC702E00BD3C4A /* HTML Standard.html */; };
624AC90F19FC702E00BD3C4A /* HTML Standard.html in Resources */ = {isa = PBXBuildFile; fileRef = 624AC90D19FC702E00BD3C4A /* HTML Standard.html */; };
+ 624B28B91B03DA820048D328 /* CSSTypeSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28B71B03DA820048D328 /* CSSTypeSelector.m */; };
+ 624B28BA1B03DA820048D328 /* CSSTypeSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28B71B03DA820048D328 /* CSSTypeSelector.m */; };
+ 624B28C01B0413200048D328 /* CSSAttributeSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28BE1B0413200048D328 /* CSSAttributeSelector.m */; };
+ 624B28C11B0413200048D328 /* CSSAttributeSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28BE1B0413200048D328 /* CSSAttributeSelector.m */; };
+ 624B28C51B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28C31B04190D0048D328 /* NSCharacterSet+HTMLKit.m */; };
+ 624B28C61B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B28C31B04190D0048D328 /* NSCharacterSet+HTMLKit.m */; };
+ 624B371F1B24E9BA0010BDDF /* CSSInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B371D1B24E9BA0010BDDF /* CSSInputStream.m */; };
+ 624B37201B24E9BA0010BDDF /* CSSInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B371D1B24E9BA0010BDDF /* CSSInputStream.m */; };
624B9FB21AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; };
624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB11AE0313300646C4C /* HTMLKitStringCategoryTests.m */; };
624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 624B9FB41AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m */; };
@@ -95,7 +102,6 @@
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 */; };
625A14C519C7829400AD0C32 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14C419C7829400AD0C32 /* XCTest.framework */; };
625A14C619C7829400AD0C32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AF19C7829400AD0C32 /* Cocoa.framework */; };
625A14C919C7829400AD0C32 /* HTMLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14AC19C7829400AD0C32 /* HTMLKit.framework */; };
@@ -103,48 +109,102 @@
625A14E019C7834100AD0C32 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DF19C7834100AD0C32 /* Foundation.framework */; };
625A14ED19C7834100AD0C32 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14C419C7829400AD0C32 /* XCTest.framework */; };
625A14EE19C7834100AD0C32 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A14DF19C7834100AD0C32 /* Foundation.framework */; };
- 625A150219C783DE00AD0C32 /* HTMLKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A14BD19C7829400AD0C32 /* HTMLKit.m */; };
625A150319C783E200AD0C32 /* HTMLKit.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; };
- 625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */; };
625A150919C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */; };
625A150A19C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */; };
- 6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87219E1808D00F12EE5 /* HTMLElement.h */; };
+ 626652F91C03D7AC00C3F121 /* HTMLKitErrorDomain.h in Headers */ = {isa = PBXBuildFile; fileRef = 626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */; settings = {ATTRIBUTES = (Public, ); }; };
6279F87519E1808D00F12EE5 /* HTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 6279F87319E1808D00F12EE5 /* HTMLElement.m */; };
6279F87619E1808D00F12EE5 /* HTMLElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 6279F87319E1808D00F12EE5 /* HTMLElement.m */; };
- 62AE593519F97D880043F069 /* HTMLParseErrorToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593319F97D880043F069 /* HTMLParseErrorToken.h */; };
+ 628AF62F1BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */; };
+ 628AF6301BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */; };
+ 628AF6341BC9A63D00496128 /* CSSNthExpressionSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF6321BC9A63D00496128 /* CSSNthExpressionSelector.m */; };
+ 628AF6351BC9A63D00496128 /* CSSNthExpressionSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF6321BC9A63D00496128 /* CSSNthExpressionSelector.m */; };
+ 628AF63F1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF63D1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m */; };
+ 628AF6401BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF63D1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m */; };
+ 629AA0EA1BDD9C2900D5CC98 /* HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 625A14BC19C7829400AD0C32 /* HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0EB1BDD9C2900D5CC98 /* HTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F873E919E088C90062683C /* HTMLParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0EC1BDD9C2900D5CC98 /* HTMLDOM.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3F1A9FDE8A00301989 /* HTMLDOM.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0ED1BDD9C2900D5CC98 /* HTMLNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234584C1A9D2FA4009BD491 /* HTMLNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0EE1BDD9C2900D5CC98 /* HTMLDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238578F1A9E772B003A45D9 /* HTMLDocument.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0EF1BDD9C2900D5CC98 /* HTMLDocumentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857941A9E8606003A45D9 /* HTMLDocumentType.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F01BDD9C2900D5CC98 /* HTMLDocumentFragment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F11BDD9C2900D5CC98 /* HTMLElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87219E1808D00F12EE5 /* HTMLElement.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F21BDD9C2900D5CC98 /* HTMLComment.h in Headers */ = {isa = PBXBuildFile; fileRef = 623857991A9E8934003A45D9 /* HTMLComment.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F31BDD9C2900D5CC98 /* HTMLText.h in Headers */ = {isa = PBXBuildFile; fileRef = 62362A3A1A9FA70400301989 /* HTMLText.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F41BDD9C2A00D5CC98 /* HTMLTemplate.h in Headers */ = {isa = PBXBuildFile; fileRef = 623406DF1ADB04F9004677A3 /* HTMLTemplate.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F51BDD9C2A00D5CC98 /* HTMLNodeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9411B152F4F00CCF25C /* HTMLNodeIterator.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F61BDD9C2A00D5CC98 /* HTMLTreeWalker.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717BA1B22009200B38302 /* HTMLTreeWalker.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F71BDD9C2A00D5CC98 /* HTMLNodeTraversal.h in Headers */ = {isa = PBXBuildFile; fileRef = 624717161B22333200C11912 /* HTMLNodeTraversal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F81BDD9C2A00D5CC98 /* HTMLNodeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6247A9461B152F8C00CCF25C /* HTMLNodeFilter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0F91BDD9C2A00D5CC98 /* HTMLKitDOMExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238C9831AB8D6330006512E /* HTMLKitDOMExceptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0FA1BDD9C2A00D5CC98 /* HTMLNamespaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0FB1BDD9C2A00D5CC98 /* HTMLQuirksMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0FC1BDD9C2A00D5CC98 /* NSString+HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE991AA509430026937B /* NSString+HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0FD1BDD9C2A00D5CC98 /* NSCharacterSet+HTMLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B28C21B04190D0048D328 /* NSCharacterSet+HTMLKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0FE1BDD9C2A00D5CC98 /* HTMLOrderedDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 623424861AB467B200726190 /* HTMLOrderedDictionary.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA0FF1BDD9C2A00D5CC98 /* CSSSelectors.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AB3151B04EA4200F3830D /* CSSSelectors.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1001BDD9C2A00D5CC98 /* CSSSelectorParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 622BF57F1BBF2FD700363583 /* CSSSelectorParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1011BDD9C2A00D5CC98 /* CSSSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 62079BE71AF56F1E00D3B402 /* CSSSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1021BDD9C2B00D5CC98 /* CSSSelectorBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 62FA98061BD5A7E200A2AF86 /* CSSSelectorBlock.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1031BDD9C2B00D5CC98 /* CSSTypeSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B28B61B03DA820048D328 /* CSSTypeSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1041BDD9C2B00D5CC98 /* CSSAttributeSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B28BD1B0413200048D328 /* CSSAttributeSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1051BDD9C2B00D5CC98 /* CSSPseudoClassSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 620EE9471BC46F2A0028ED34 /* CSSPseudoClassSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1061BDD9C2B00D5CC98 /* CSSStructuralPseudoSelectors.h in Headers */ = {isa = PBXBuildFile; fileRef = 628AF63C1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1071BDD9C2B00D5CC98 /* CSSNthExpressionSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 628AF6311BC9A63D00496128 /* CSSNthExpressionSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1081BDD9C2B00D5CC98 /* CSSPseudoFunctionSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 62ACB8DB1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA1091BDD9C2B00D5CC98 /* CSSCombinatorSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 62FA04E11BCC360D009ABF98 /* CSSCombinatorSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA10A1BDD9C2B00D5CC98 /* CSSCompoundSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = 620C87791BD44CBE00FB3EEE /* CSSCompoundSelector.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 629AA10B1BDD9C5200D5CC98 /* HTMLTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A419CCC54100BCDDF4 /* HTMLTokenizer.h */; };
+ 629AA10C1BDD9C5200D5CC98 /* HTMLTokenizerStates.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493A919CCE84A00BCDDF4 /* HTMLTokenizerStates.h */; };
+ 629AA10D1BDD9C5300D5CC98 /* HTMLTokenizerCharacters.h in Headers */ = {isa = PBXBuildFile; fileRef = 62E7CAAE19CDFFB500465A83 /* HTMLTokenizerCharacters.h */; };
+ 629AA10E1BDD9C5300D5CC98 /* HTMLTokenizerEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F31FDB19E9DCCF007F0657 /* HTMLTokenizerEntities.h */; };
+ 629AA10F1BDD9C5300D5CC98 /* HTMLTokens.h in Headers */ = {isa = PBXBuildFile; fileRef = 624AC8FE19FBF4F700BD3C4A /* HTMLTokens.h */; };
+ 629AA1101BDD9C5300D5CC98 /* HTMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 624493AA19CD0CBE00BCDDF4 /* HTMLToken.h */; };
+ 629AA1111BDD9C5300D5CC98 /* HTMLCharacterToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594719F9948A0043F069 /* HTMLCharacterToken.h */; };
+ 629AA1121BDD9C5300D5CC98 /* HTMLCommentToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594219F992F30043F069 /* HTMLCommentToken.h */; };
+ 629AA1131BDD9C5300D5CC98 /* HTMLDOCTYPEToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */; };
+ 629AA1141BDD9C5400D5CC98 /* HTMLParseErrorToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593319F97D880043F069 /* HTMLParseErrorToken.h */; };
+ 629AA1151BDD9C5400D5CC98 /* HTMLTagToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593D19F9907C0043F069 /* HTMLTagToken.h */; };
+ 629AA1161BDD9C5400D5CC98 /* HTMLEOFToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 6238039D1AB63A8C008A53D0 /* HTMLEOFToken.h */; };
+ 629AA1171BDD9C5400D5CC98 /* HTMLStackOfOpenElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234BEEC1AABBF1400DEB15F /* HTMLStackOfOpenElements.h */; };
+ 629AA1181BDD9C5400D5CC98 /* HTMLListOfActiveFormattingElements.h in Headers */ = {isa = PBXBuildFile; fileRef = 62363C3B1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.h */; };
+ 629AA1191BDD9C5400D5CC98 /* HTMLElementTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */; };
+ 629AA11A1BDD9C5400D5CC98 /* HTMLElementAdjustment.h in Headers */ = {isa = PBXBuildFile; fileRef = 6234C3361AB3BF710046F527 /* HTMLElementAdjustment.h */; };
+ 629AA11B1BDD9C5500D5CC98 /* HTMLParserInsertionModes.h in Headers */ = {isa = PBXBuildFile; fileRef = 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */; };
+ 629AA11C1BDD9C5500D5CC98 /* HTMLMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = 6235CE9E1AA5170A0026937B /* HTMLMarker.h */; };
+ 629AA11D1BDD9C5500D5CC98 /* CSSCodePoints.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B37211B250BB50010BDDF /* CSSCodePoints.h */; };
+ 629AA11E1BDD9C5500D5CC98 /* CSSInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 624B371C1B24E9BA0010BDDF /* CSSInputStream.h */; };
+ 629AA11F1BDD9C5E00D5CC98 /* CSSNthExpressionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F501631BC9576B0069F47B /* CSSNthExpressionParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
62AE593619F97D880043F069 /* HTMLParseErrorToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593419F97D880043F069 /* HTMLParseErrorToken.m */; };
62AE593719F97D880043F069 /* HTMLParseErrorToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593419F97D880043F069 /* HTMLParseErrorToken.m */; };
- 62AE593A19F97E1C0043F069 /* HTMLDOCTYPEToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */; };
62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593919F97E1C0043F069 /* HTMLDOCTYPEToken.m */; };
62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593919F97E1C0043F069 /* HTMLDOCTYPEToken.m */; };
- 62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE593D19F9907C0043F069 /* HTMLTagToken.h */; };
62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593E19F9907C0043F069 /* HTMLTagToken.m */; };
62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE593E19F9907C0043F069 /* HTMLTagToken.m */; };
- 62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594219F992F30043F069 /* HTMLCommentToken.h */; };
62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594319F992F30043F069 /* HTMLCommentToken.m */; };
62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594319F992F30043F069 /* HTMLCommentToken.m */; };
- 62AE594919F9948A0043F069 /* HTMLCharacterToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 62AE594719F9948A0043F069 /* HTMLCharacterToken.h */; };
62AE594A19F9948A0043F069 /* HTMLCharacterToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 62AE594819F9948A0043F069 /* HTMLCharacterToken.m */; };
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 /* 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 */; };
- 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 */; };
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 */; };
- 62F873EB19E088C90062683C /* HTMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 62F873E919E088C90062683C /* HTMLParser.h */; };
+ 62F501661BC9576B0069F47B /* CSSNthExpressionParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */; };
+ 62F501671BC9576B0069F47B /* CSSNthExpressionParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */; };
+ 62F658701BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */; };
+ 62F658711BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */; };
62F873EC19E088C90062683C /* HTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F873EA19E088C90062683C /* HTMLParser.m */; };
62F873ED19E088C90062683C /* HTMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 62F873EA19E088C90062683C /* HTMLParser.m */; };
+ 62FA04E41BCC360D009ABF98 /* CSSCombinatorSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA04E21BCC360D009ABF98 /* CSSCombinatorSelector.m */; };
+ 62FA04E51BCC360D009ABF98 /* CSSCombinatorSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA04E21BCC360D009ABF98 /* CSSCombinatorSelector.m */; };
+ 62FA98041BD57B6200A2AF86 /* CSSSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA98031BD57B6200A2AF86 /* CSSSelectors.m */; };
+ 62FA98051BD57B6200A2AF86 /* CSSSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA98031BD57B6200A2AF86 /* CSSSelectors.m */; };
+ 62FA98091BD5A7E200A2AF86 /* CSSSelectorBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA98071BD5A7E200A2AF86 /* CSSSelectorBlock.m */; };
+ 62FA980A1BD5A7E200A2AF86 /* CSSSelectorBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FA98071BD5A7E200A2AF86 /* CSSSelectorBlock.m */; };
+ 62FC60461BD0519B0042BBE7 /* CSSSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FC60451BD0519B0042BBE7 /* CSSSelector.m */; };
+ 62FC60471BD0519B0042BBE7 /* CSSSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FC60451BD0519B0042BBE7 /* CSSSelector.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -178,7 +238,19 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 62079BE71AF56F1E00D3B402 /* CSSSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelector.h; sourceTree = ""; };
+ 620C87791BD44CBE00FB3EEE /* CSSCompoundSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSCompoundSelector.h; sourceTree = ""; };
+ 620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSCompoundSelector.m; sourceTree = ""; };
+ 620EE9471BC46F2A0028ED34 /* CSSPseudoClassSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSPseudoClassSelector.h; sourceTree = ""; };
+ 620EE9481BC46F2A0028ED34 /* CSSPseudoClassSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSPseudoClassSelector.m; sourceTree = ""; };
+ 62132E571C01F83200084175 /* CSSSelectorTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelectorTest.h; sourceTree = ""; };
+ 62132E581C01F83200084175 /* CSSSelectorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorTest.m; sourceTree = ""; };
+ 62132E5B1C021FF200084175 /* css-tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "css-tests"; sourceTree = ""; };
+ 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorParserTests.m; sourceTree = ""; };
+ 621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSCombinatorSelectorTests.m; sourceTree = ""; };
6223211D1A969B9300BACED5 /* HTMLElementTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLElementTypes.h; sourceTree = ""; };
+ 622BF57F1BBF2FD700363583 /* CSSSelectorParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelectorParser.h; sourceTree = ""; };
+ 622BF5801BBF2FD700363583 /* CSSSelectorParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorParser.m; sourceTree = ""; };
623406DF1ADB04F9004677A3 /* HTMLTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLTemplate.h; sourceTree = ""; };
623406E01ADB04F9004677A3 /* HTMLTemplate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLTemplate.m; sourceTree = ""; };
623406E41ADB05AD004677A3 /* HTMLDocumentFragment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDocumentFragment.h; sourceTree = ""; };
@@ -230,10 +302,22 @@
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 = ""; };
+ 624AB3151B04EA4200F3830D /* CSSSelectors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSSelectors.h; sourceTree = ""; };
+ 624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSTypeSelectorTests.m; sourceTree = ""; };
+ 624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSAttributeSelectorTests.m; 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 = ""; };
624AC90D19FC702E00BD3C4A /* HTML Standard.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "HTML Standard.html"; sourceTree = ""; };
+ 624B28B61B03DA820048D328 /* CSSTypeSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSTypeSelector.h; sourceTree = ""; };
+ 624B28B71B03DA820048D328 /* CSSTypeSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSTypeSelector.m; sourceTree = ""; };
+ 624B28BD1B0413200048D328 /* CSSAttributeSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSAttributeSelector.h; sourceTree = ""; };
+ 624B28BE1B0413200048D328 /* CSSAttributeSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSAttributeSelector.m; sourceTree = ""; };
+ 624B28C21B04190D0048D328 /* NSCharacterSet+HTMLKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSCharacterSet+HTMLKit.h"; sourceTree = ""; };
+ 624B28C31B04190D0048D328 /* NSCharacterSet+HTMLKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSCharacterSet+HTMLKit.m"; sourceTree = ""; };
+ 624B371C1B24E9BA0010BDDF /* CSSInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSInputStream.h; sourceTree = ""; };
+ 624B371D1B24E9BA0010BDDF /* CSSInputStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSInputStream.m; sourceTree = ""; };
+ 624B37211B250BB50010BDDF /* CSSCodePoints.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSCodePoints.h; 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 = ""; };
@@ -246,7 +330,6 @@
625A14B919C7829400AD0C32 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
625A14BB19C7829400AD0C32 /* HTMLKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HTMLKit-Prefix.pch"; sourceTree = ""; };
625A14BC19C7829400AD0C32 /* HTMLKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLKit.h; sourceTree = ""; };
- 625A14BD19C7829400AD0C32 /* HTMLKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HTMLKit.m; sourceTree = ""; };
625A14C319C7829400AD0C32 /* HTMLKitFrameworkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HTMLKitFrameworkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
625A14C419C7829400AD0C32 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
625A14CC19C7829400AD0C32 /* HTMLKitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HTMLKitTests-Info.plist"; sourceTree = ""; };
@@ -256,10 +339,18 @@
625A14EC19C7834100AD0C32 /* HTMLKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HTMLKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLInputStreamReader.h; sourceTree = ""; };
625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLInputStreamReader.m; sourceTree = ""; };
+ 626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLKitErrorDomain.h; sourceTree = ""; };
6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLParserInsertionModes.h; sourceTree = ""; };
6279F87219E1808D00F12EE5 /* HTMLElement.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; path = HTMLElement.h; sourceTree = ""; };
6279F87319E1808D00F12EE5 /* HTMLElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLElement.m; sourceTree = ""; };
+ 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNthExpressionsParserTests.m; sourceTree = ""; };
+ 628AF6311BC9A63D00496128 /* CSSNthExpressionSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSNthExpressionSelector.h; sourceTree = ""; };
+ 628AF6321BC9A63D00496128 /* CSSNthExpressionSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNthExpressionSelector.m; sourceTree = ""; };
+ 628AF63C1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSStructuralPseudoSelectors.h; sourceTree = ""; };
+ 628AF63D1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSStructuralPseudoSelectors.m; sourceTree = ""; };
628B7CE61A080E1000602C87 /* HTMLNamespaces.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLNamespaces.h; sourceTree = ""; };
+ 62ACB8DB1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSPseudoFunctionSelector.h; sourceTree = ""; };
+ 62ACB8DC1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSPseudoFunctionSelector.m; sourceTree = ""; };
62AE593319F97D880043F069 /* HTMLParseErrorToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLParseErrorToken.h; sourceTree = ""; };
62AE593419F97D880043F069 /* HTMLParseErrorToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLParseErrorToken.m; sourceTree = ""; };
62AE593819F97E1C0043F069 /* HTMLDOCTYPEToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDOCTYPEToken.h; sourceTree = ""; };
@@ -276,8 +367,17 @@
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 = ""; };
+ 62F501631BC9576B0069F47B /* CSSNthExpressionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSNthExpressionParser.h; sourceTree = ""; };
+ 62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNthExpressionParser.m; sourceTree = ""; };
+ 62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSNThExpressionSelectorTests.m; sourceTree = ""; };
62F873E919E088C90062683C /* HTMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLParser.h; sourceTree = ""; };
62F873EA19E088C90062683C /* HTMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLParser.m; sourceTree = ""; };
+ 62FA04E11BCC360D009ABF98 /* CSSCombinatorSelector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSCombinatorSelector.h; sourceTree = ""; };
+ 62FA04E21BCC360D009ABF98 /* CSSCombinatorSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSCombinatorSelector.m; sourceTree = ""; };
+ 62FA98031BD57B6200A2AF86 /* CSSSelectors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectors.m; sourceTree = ""; };
+ 62FA98061BD5A7E200A2AF86 /* CSSSelectorBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSSelectorBlock.h; sourceTree = ""; };
+ 62FA98071BD5A7E200A2AF86 /* CSSSelectorBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelectorBlock.m; sourceTree = ""; };
+ 62FC60451BD0519B0042BBE7 /* CSSSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSSelector.m; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -320,6 +420,42 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 62079BE61AF5656600D3B402 /* Selectors */ = {
+ isa = PBXGroup;
+ children = (
+ 624AB3151B04EA4200F3830D /* CSSSelectors.h */,
+ 62FA98031BD57B6200A2AF86 /* CSSSelectors.m */,
+ 624B37211B250BB50010BDDF /* CSSCodePoints.h */,
+ 624B371C1B24E9BA0010BDDF /* CSSInputStream.h */,
+ 624B371D1B24E9BA0010BDDF /* CSSInputStream.m */,
+ 622BF57F1BBF2FD700363583 /* CSSSelectorParser.h */,
+ 622BF5801BBF2FD700363583 /* CSSSelectorParser.m */,
+ 62F501631BC9576B0069F47B /* CSSNthExpressionParser.h */,
+ 62F501641BC9576B0069F47B /* CSSNthExpressionParser.m */,
+ 62079BE71AF56F1E00D3B402 /* CSSSelector.h */,
+ 62FC60451BD0519B0042BBE7 /* CSSSelector.m */,
+ 62FA98061BD5A7E200A2AF86 /* CSSSelectorBlock.h */,
+ 62FA98071BD5A7E200A2AF86 /* CSSSelectorBlock.m */,
+ 624B28B61B03DA820048D328 /* CSSTypeSelector.h */,
+ 624B28B71B03DA820048D328 /* CSSTypeSelector.m */,
+ 624B28BD1B0413200048D328 /* CSSAttributeSelector.h */,
+ 624B28BE1B0413200048D328 /* CSSAttributeSelector.m */,
+ 620EE9471BC46F2A0028ED34 /* CSSPseudoClassSelector.h */,
+ 620EE9481BC46F2A0028ED34 /* CSSPseudoClassSelector.m */,
+ 628AF63C1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.h */,
+ 628AF63D1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m */,
+ 628AF6311BC9A63D00496128 /* CSSNthExpressionSelector.h */,
+ 628AF6321BC9A63D00496128 /* CSSNthExpressionSelector.m */,
+ 62ACB8DB1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.h */,
+ 62ACB8DC1BC5C13E00E283C1 /* CSSPseudoFunctionSelector.m */,
+ 62FA04E11BCC360D009ABF98 /* CSSCombinatorSelector.h */,
+ 62FA04E21BCC360D009ABF98 /* CSSCombinatorSelector.m */,
+ 620C87791BD44CBE00FB3EEE /* CSSCompoundSelector.h */,
+ 620C877A1BD44CBE00FB3EEE /* CSSCompoundSelector.m */,
+ );
+ name = Selectors;
+ sourceTree = "";
+ };
6236738C1AC0CD2400FF89B3 /* Tokenizer */ = {
isa = PBXGroup;
children = (
@@ -451,12 +587,13 @@
isa = PBXGroup;
children = (
625A14BC19C7829400AD0C32 /* HTMLKit.h */,
- 625A14BD19C7829400AD0C32 /* HTMLKit.m */,
+ 626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */,
62AE593219F97CCA0043F069 /* Tokenizing */,
628E16EC1ADAE71700B15A06 /* Parsing */,
623719441AA1472B002E03C8 /* DOM */,
628E16ED1ADAE73700B15A06 /* Categories */,
628E16EE1ADAE75300B15A06 /* Structures */,
+ 62079BE61AF5656600D3B402 /* Selectors */,
625A14B619C7829400AD0C32 /* Supporting Files */,
);
name = "Source Code";
@@ -482,6 +619,14 @@
624B9FB81AE072D500646C4C /* Categories */,
624E1A2D1B1D1C8A00E66AAC /* Structures */,
625A14CB19C7829400AD0C32 /* Supporting Files */,
+ 62132E571C01F83200084175 /* CSSSelectorTest.h */,
+ 62132E581C01F83200084175 /* CSSSelectorTest.m */,
+ 621FBE591BDAD68700BC9555 /* CSSSelectorParserTests.m */,
+ 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */,
+ 624AB3161B0508AE00F3830D /* CSSTypeSelectorTests.m */,
+ 624AB3191B050A4D00F3830D /* CSSAttributeSelectorTests.m */,
+ 62F6586F1BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m */,
+ 621FBE5C1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m */,
);
name = Tests;
path = HTMLKitTests;
@@ -490,6 +635,7 @@
625A14CB19C7829400AD0C32 /* Supporting Files */ = {
isa = PBXGroup;
children = (
+ 62132E5B1C021FF200084175 /* css-tests */,
624AC90D19FC702E00BD3C4A /* HTML Standard.html */,
624AC90419FBFE8A00BD3C4A /* html5lib-tests */,
625A14CC19C7829400AD0C32 /* HTMLKitTests-Info.plist */,
@@ -521,6 +667,8 @@
children = (
6235CE991AA509430026937B /* NSString+HTMLKit.h */,
6235CE9A1AA509430026937B /* NSString+HTMLKit.m */,
+ 624B28C21B04190D0048D328 /* NSCharacterSet+HTMLKit.h */,
+ 624B28C31B04190D0048D328 /* NSCharacterSet+HTMLKit.m */,
);
name = Categories;
sourceTree = "";
@@ -571,45 +719,61 @@
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 */,
- 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 /* HTMLDOM.h in Headers */,
- 62F873EB19E088C90062683C /* HTMLParser.h in Headers */,
- 624AC8FF19FBF59800BD3C4A /* HTMLTokens.h in Headers */,
- 62AE594419F992F30043F069 /* HTMLCommentToken.h in Headers */,
- 624493AC19CD0CBE00BCDDF4 /* HTMLToken.h in Headers */,
- 6235CEA01AA5170A0026937B /* HTMLMarker.h in Headers */,
- 62F31FDD19E9DCCF007F0657 /* HTMLTokenizerEntities.h in Headers */,
- 623406E11ADB04F9004677A3 /* HTMLTemplate.h in Headers */,
- 6234BEEE1AABBF1400DEB15F /* HTMLStackOfOpenElements.h in Headers */,
- 6238C9851AB8D6330006512E /* HTMLKitDOMExceptions.h in Headers */,
- 6238039F1AB63A8C008A53D0 /* HTMLEOFToken.h in Headers */,
- 6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */,
- 62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */,
- 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 */,
- 6247A9431B152F4F00CCF25C /* HTMLNodeIterator.h in Headers */,
- 625A150419C783EB00AD0C32 /* HTMLKit.h in Headers */,
- 625A150819C78ABA00AD0C32 /* HTMLInputStreamReader.h in Headers */,
- 6238579B1A9E8934003A45D9 /* HTMLComment.h in Headers */,
- 62AE593519F97D880043F069 /* HTMLParseErrorToken.h in Headers */,
- 62AE594919F9948A0043F069 /* HTMLCharacterToken.h in Headers */,
- 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 */,
+ 629AA0EA1BDD9C2900D5CC98 /* HTMLKit.h in Headers */,
+ 626652F91C03D7AC00C3F121 /* HTMLKitErrorDomain.h in Headers */,
+ 629AA0EB1BDD9C2900D5CC98 /* HTMLParser.h in Headers */,
+ 629AA0EC1BDD9C2900D5CC98 /* HTMLDOM.h in Headers */,
+ 629AA0ED1BDD9C2900D5CC98 /* HTMLNode.h in Headers */,
+ 629AA0EE1BDD9C2900D5CC98 /* HTMLDocument.h in Headers */,
+ 629AA0EF1BDD9C2900D5CC98 /* HTMLDocumentType.h in Headers */,
+ 629AA0F01BDD9C2900D5CC98 /* HTMLDocumentFragment.h in Headers */,
+ 629AA0F11BDD9C2900D5CC98 /* HTMLElement.h in Headers */,
+ 629AA0F21BDD9C2900D5CC98 /* HTMLComment.h in Headers */,
+ 629AA0F31BDD9C2900D5CC98 /* HTMLText.h in Headers */,
+ 629AA0F41BDD9C2A00D5CC98 /* HTMLTemplate.h in Headers */,
+ 629AA0F51BDD9C2A00D5CC98 /* HTMLNodeIterator.h in Headers */,
+ 629AA0F61BDD9C2A00D5CC98 /* HTMLTreeWalker.h in Headers */,
+ 629AA0F71BDD9C2A00D5CC98 /* HTMLNodeTraversal.h in Headers */,
+ 629AA0F81BDD9C2A00D5CC98 /* HTMLNodeFilter.h in Headers */,
+ 629AA0F91BDD9C2A00D5CC98 /* HTMLKitDOMExceptions.h in Headers */,
+ 629AA0FA1BDD9C2A00D5CC98 /* HTMLNamespaces.h in Headers */,
+ 629AA0FB1BDD9C2A00D5CC98 /* HTMLQuirksMode.h in Headers */,
+ 629AA0FC1BDD9C2A00D5CC98 /* NSString+HTMLKit.h in Headers */,
+ 629AA0FD1BDD9C2A00D5CC98 /* NSCharacterSet+HTMLKit.h in Headers */,
+ 629AA0FE1BDD9C2A00D5CC98 /* HTMLOrderedDictionary.h in Headers */,
+ 629AA0FF1BDD9C2A00D5CC98 /* CSSSelectors.h in Headers */,
+ 629AA1001BDD9C2A00D5CC98 /* CSSSelectorParser.h in Headers */,
+ 629AA11F1BDD9C5E00D5CC98 /* CSSNthExpressionParser.h in Headers */,
+ 629AA1011BDD9C2A00D5CC98 /* CSSSelector.h in Headers */,
+ 629AA1021BDD9C2B00D5CC98 /* CSSSelectorBlock.h in Headers */,
+ 629AA1031BDD9C2B00D5CC98 /* CSSTypeSelector.h in Headers */,
+ 629AA1041BDD9C2B00D5CC98 /* CSSAttributeSelector.h in Headers */,
+ 629AA1051BDD9C2B00D5CC98 /* CSSPseudoClassSelector.h in Headers */,
+ 629AA1061BDD9C2B00D5CC98 /* CSSStructuralPseudoSelectors.h in Headers */,
+ 629AA1071BDD9C2B00D5CC98 /* CSSNthExpressionSelector.h in Headers */,
+ 629AA1081BDD9C2B00D5CC98 /* CSSPseudoFunctionSelector.h in Headers */,
+ 629AA1091BDD9C2B00D5CC98 /* CSSCombinatorSelector.h in Headers */,
+ 629AA10A1BDD9C2B00D5CC98 /* CSSCompoundSelector.h in Headers */,
+ 629AA10B1BDD9C5200D5CC98 /* HTMLTokenizer.h in Headers */,
+ 629AA10C1BDD9C5200D5CC98 /* HTMLTokenizerStates.h in Headers */,
+ 629AA10D1BDD9C5300D5CC98 /* HTMLTokenizerCharacters.h in Headers */,
+ 629AA10E1BDD9C5300D5CC98 /* HTMLTokenizerEntities.h in Headers */,
+ 629AA10F1BDD9C5300D5CC98 /* HTMLTokens.h in Headers */,
+ 629AA1101BDD9C5300D5CC98 /* HTMLToken.h in Headers */,
+ 629AA1111BDD9C5300D5CC98 /* HTMLCharacterToken.h in Headers */,
+ 629AA1121BDD9C5300D5CC98 /* HTMLCommentToken.h in Headers */,
+ 629AA1131BDD9C5300D5CC98 /* HTMLDOCTYPEToken.h in Headers */,
+ 629AA1141BDD9C5400D5CC98 /* HTMLParseErrorToken.h in Headers */,
+ 629AA1151BDD9C5400D5CC98 /* HTMLTagToken.h in Headers */,
+ 629AA1161BDD9C5400D5CC98 /* HTMLEOFToken.h in Headers */,
+ 629AA1171BDD9C5400D5CC98 /* HTMLStackOfOpenElements.h in Headers */,
+ 629AA1181BDD9C5400D5CC98 /* HTMLListOfActiveFormattingElements.h in Headers */,
+ 629AA1191BDD9C5400D5CC98 /* HTMLElementTypes.h in Headers */,
+ 629AA11A1BDD9C5400D5CC98 /* HTMLElementAdjustment.h in Headers */,
+ 629AA11B1BDD9C5500D5CC98 /* HTMLParserInsertionModes.h in Headers */,
+ 629AA11C1BDD9C5500D5CC98 /* HTMLMarker.h in Headers */,
+ 629AA11D1BDD9C5500D5CC98 /* CSSCodePoints.h in Headers */,
+ 629AA11E1BDD9C5500D5CC98 /* CSSInputStream.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -693,7 +857,7 @@
625A14A319C7829400AD0C32 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0610;
+ LastUpgradeCheck = 0700;
ORGANIZATIONNAME = BrainCookie;
TargetAttributes = {
625A14C219C7829400AD0C32 = {
@@ -739,6 +903,7 @@
files = (
624AC90F19FC702E00BD3C4A /* HTML Standard.html in Resources */,
624AC90519FBFE9A00BD3C4A /* html5lib-tests in Resources */,
+ 62132E5D1C021FF200084175 /* css-tests in Resources */,
625A14CF19C7829400AD0C32 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -747,6 +912,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 62132E5C1C021FF200084175 /* css-tests in Resources */,
624AC90619FBFEA000BD3C4A /* html5lib-tests in Resources */,
624AC90E19FC702E00BD3C4A /* HTML Standard.html in Resources */,
);
@@ -759,34 +925,48 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 62F501671BC9576B0069F47B /* CSSNthExpressionParser.m in Sources */,
62362A3E1A9FA70400301989 /* HTMLText.m in Sources */,
+ 622BF5831BBF2FD700363583 /* CSSSelectorParser.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 */,
6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */,
+ 62FA98051BD57B6200A2AF86 /* CSSSelectors.m in Sources */,
6234248A1AB467B200726190 /* HTMLOrderedDictionary.m in Sources */,
624717BE1B22009200B38302 /* HTMLTreeWalker.m in Sources */,
62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */,
+ 624B37201B24E9BA0010BDDF /* CSSInputStream.m in Sources */,
62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */,
62363C3F1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */,
62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */,
623406E31ADB04F9004677A3 /* HTMLTemplate.m in Sources */,
+ 62FA04E51BCC360D009ABF98 /* CSSCombinatorSelector.m in Sources */,
6238C9871AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */,
+ 62FA980A1BD5A7E200A2AF86 /* CSSSelectorBlock.m in Sources */,
+ 628AF6401BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m in Sources */,
6247171A1B22333200C11912 /* HTMLNodeTraversal.m in Sources */,
62F873ED19E088C90062683C /* HTMLParser.m in Sources */,
623803A11AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */,
+ 624B28C61B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */,
6279F87619E1808D00F12EE5 /* HTMLElement.m in Sources */,
+ 620C877D1BD44CBE00FB3EEE /* CSSCompoundSelector.m in Sources */,
625A150A19C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */,
+ 628AF6351BC9A63D00496128 /* CSSNthExpressionSelector.m in Sources */,
623857931A9E772B003A45D9 /* HTMLDocument.m in Sources */,
623406E81ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */,
624717B91B21FE5400B38302 /* HTMLNodeFilter.m in Sources */,
+ 620EE94B1BC46F2A0028ED34 /* CSSPseudoClassSelector.m in Sources */,
624493A819CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */,
- 625A14BE19C7829400AD0C32 /* HTMLKit.m in Sources */,
+ 624B28BA1B03DA820048D328 /* CSSTypeSelector.m in Sources */,
+ 62FC60471BD0519B0042BBE7 /* CSSSelector.m in Sources */,
623857981A9E8606003A45D9 /* HTMLDocumentType.m in Sources */,
+ 620C877F1BD4519A00FB3EEE /* CSSPseudoFunctionSelector.m in Sources */,
62AE594B19F9948A0043F069 /* HTMLCharacterToken.m in Sources */,
623458501A9D2FA4009BD491 /* HTMLNode.m in Sources */,
+ 624B28C11B0413200048D328 /* CSSAttributeSelector.m in Sources */,
62F31FDF19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */,
62AE593719F97D880043F069 /* HTMLParseErrorToken.m in Sources */,
);
@@ -796,11 +976,15 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 62132E5A1C01F83200084175 /* CSSSelectorTest.m in Sources */,
6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */,
+ 62F658711BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */,
623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */,
6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */,
62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */,
+ 624AB31B1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */,
624B9FB61AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */,
+ 624AB3181B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */,
6236738F1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */,
624AC90219FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */,
624B9FB31AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */,
@@ -808,6 +992,9 @@
62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */,
6239755F1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */,
624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */,
+ 621FBE5B1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */,
+ 621FBE5E1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */,
+ 628AF6301BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -816,6 +1003,7 @@
buildActionMask = 2147483647;
files = (
62362A3D1A9FA70400301989 /* HTMLText.m in Sources */,
+ 622BF5821BBF2FD700363583 /* CSSSelectorParser.m in Sources */,
6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */,
6247A9441B152F4F00CCF25C /* HTMLNodeIterator.m in Sources */,
6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */,
@@ -823,26 +1011,39 @@
624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */,
6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */,
623424891AB467B200726190 /* HTMLOrderedDictionary.m in Sources */,
+ 62FA98041BD57B6200A2AF86 /* CSSSelectors.m in Sources */,
624717BD1B22009200B38302 /* HTMLTreeWalker.m in Sources */,
62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */,
+ 62F501661BC9576B0069F47B /* CSSNthExpressionParser.m in Sources */,
+ 624B371F1B24E9BA0010BDDF /* CSSInputStream.m in Sources */,
62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */,
62363C3E1ABE428200DAB4C6 /* HTMLListOfActiveFormattingElements.m in Sources */,
62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */,
623406E21ADB04F9004677A3 /* HTMLTemplate.m in Sources */,
+ 62FA04E41BCC360D009ABF98 /* CSSCombinatorSelector.m in Sources */,
+ 628AF63F1BC9D6FB00496128 /* CSSStructuralPseudoSelectors.m in Sources */,
+ 62FA98091BD5A7E200A2AF86 /* CSSSelectorBlock.m in Sources */,
6238C9861AB8D6330006512E /* HTMLKitDOMExceptions.m in Sources */,
624717191B22333200C11912 /* HTMLNodeTraversal.m in Sources */,
62F873EC19E088C90062683C /* HTMLParser.m in Sources */,
623803A01AB63A8C008A53D0 /* HTMLEOFToken.m in Sources */,
+ 624B28C51B04190D0048D328 /* NSCharacterSet+HTMLKit.m in Sources */,
6279F87519E1808D00F12EE5 /* HTMLElement.m in Sources */,
+ 620C877C1BD44CBE00FB3EEE /* CSSCompoundSelector.m in Sources */,
625A150919C78ABA00AD0C32 /* HTMLInputStreamReader.m in Sources */,
623857921A9E772B003A45D9 /* HTMLDocument.m in Sources */,
623406E71ADB05AD004677A3 /* HTMLDocumentFragment.m in Sources */,
624717B81B21FE5400B38302 /* HTMLNodeFilter.m in Sources */,
+ 620EE94A1BC46F2A0028ED34 /* CSSPseudoClassSelector.m in Sources */,
624493A719CCC54100BCDDF4 /* HTMLTokenizer.m in Sources */,
- 625A150219C783DE00AD0C32 /* HTMLKit.m in Sources */,
+ 624B28B91B03DA820048D328 /* CSSTypeSelector.m in Sources */,
623857971A9E8606003A45D9 /* HTMLDocumentType.m in Sources */,
+ 62FC60461BD0519B0042BBE7 /* CSSSelector.m in Sources */,
62AE594A19F9948A0043F069 /* HTMLCharacterToken.m in Sources */,
+ 620C877E1BD4519A00FB3EEE /* CSSPseudoFunctionSelector.m in Sources */,
6234584F1A9D2FA4009BD491 /* HTMLNode.m in Sources */,
+ 624B28C01B0413200048D328 /* CSSAttributeSelector.m in Sources */,
+ 628AF6341BC9A63D00496128 /* CSSNthExpressionSelector.m in Sources */,
62F31FDE19E9DCCF007F0657 /* HTMLTokenizerEntities.m in Sources */,
62AE593619F97D880043F069 /* HTMLParseErrorToken.m in Sources */,
);
@@ -852,11 +1053,15 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 62132E591C01F83200084175 /* CSSSelectorTest.m in Sources */,
6239755A1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */,
+ 62F658701BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */,
623CAF9D1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */,
6247171C1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */,
62EC7AE61AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */,
+ 624AB31A1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */,
624B9FB51AE0391400646C4C /* HTMLKitOrderedDictionaryTests.m in Sources */,
+ 624AB3171B0508AE00F3830D /* CSSTypeSelectorTests.m in Sources */,
6236738E1AC0CE2500FF89B3 /* HTMLKitTokenizerPerformance.m in Sources */,
624AC90119FBF9ED00BD3C4A /* HTMLKitTokenizerTests.m in Sources */,
624B9FB21AE0313300646C4C /* HTMLKitStringCategoryTests.m in Sources */,
@@ -864,6 +1069,9 @@
62D8345919FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */,
6239755E1AC364BB007E26F1 /* HTML5LibTreeConstructionTest.m in Sources */,
624FC37A1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */,
+ 621FBE5A1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */,
+ 621FBE5D1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */,
+ 628AF62F1BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -919,6 +1127,7 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
+ ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
@@ -976,12 +1185,14 @@
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
+ DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
INFOPLIST_FILE = "HTMLKit/HTMLKit-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = HTMLKit;
WRAPPER_EXTENSION = framework;
};
@@ -991,12 +1202,14 @@
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
+ DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
INFOPLIST_FILE = "HTMLKit/HTMLKit-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = HTMLKit;
WRAPPER_EXTENSION = framework;
};
@@ -1018,6 +1231,7 @@
"$(inherited)",
);
INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -1036,6 +1250,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -1045,6 +1260,7 @@
625A14FD19C7834100AD0C32 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ DEFINES_MODULE = YES;
DSTROOT = /tmp/HTMLKit.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
@@ -1063,6 +1279,7 @@
625A14FE19C7834100AD0C32 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ DEFINES_MODULE = YES;
DSTROOT = /tmp/HTMLKit.dst;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
@@ -1091,6 +1308,8 @@
);
INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
+ OTHER_LDFLAGS = " -ObjC";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TEST_HOST = "$(BUNDLE_LOADER)";
@@ -1110,6 +1329,8 @@
GCC_PREFIX_HEADER = "HTMLKit/HTMLKit-Prefix.pch";
INFOPLIST_FILE = "HTMLKitTests/HTMLKitTests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
+ OTHER_LDFLAGS = " -ObjC";
+ PRODUCT_BUNDLE_IDENTIFIER = "com.braincookie.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
TEST_HOST = "$(BUNDLE_LOADER)";
diff --git a/HTMLKit.xcworkspace/contents.xcworkspacedata b/HTMLKit.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..da024c6
--- /dev/null
+++ b/HTMLKit.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/HTMLKit/CSSAttributeSelector.h b/HTMLKit/CSSAttributeSelector.h
new file mode 100644
index 0000000..d1ba040
--- /dev/null
+++ b/HTMLKit/CSSAttributeSelector.h
@@ -0,0 +1,30 @@
+//
+// CSSAttributeSelector.h
+// HTMLKit
+//
+// Created by Iska on 14/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSSelector.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CSSAttributeSelector : CSSSelector
+
+@property (nonatomic, assign) CSSAttributeSelectorType type;
+@property (nonatomic, strong, readonly) NSString *name;
+@property (nonatomic, strong, readonly) NSString *value;
+
++ (instancetype)classSelector:(NSString *)className;
++ (instancetype)idSelector:(NSString *)elementId;
++ (instancetype)hasAttributeSelector:(NSString *)attributeName;
+
+- (instancetype)initWithType:(CSSAttributeSelectorType)type
+ attributeName:(NSString *)name
+ attrbiuteValue:(NSString *)value;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSAttributeSelector.m b/HTMLKit/CSSAttributeSelector.m
new file mode 100644
index 0000000..b8d49f7
--- /dev/null
+++ b/HTMLKit/CSSAttributeSelector.m
@@ -0,0 +1,111 @@
+//
+// CSSAttributeSelector.m
+// HTMLKit
+//
+// Created by Iska on 14/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSAttributeSelector.h"
+#import "HTMLElement.h"
+#import "NSCharacterSet+HTMLKit.h"
+
+@interface CSSAttributeSelector ()
+{
+ CSSAttributeSelectorType _type;
+ NSString *_name;
+ NSString *_value;
+}
+@end
+
+@implementation CSSAttributeSelector
+
++ (instancetype)classSelector:(NSString *)className
+{
+ return [[self alloc] initWithType:CSSAttributeSelectorIncludes attributeName:@"class" attrbiuteValue:className];
+}
+
++ (instancetype)idSelector:(NSString *)elementId
+{
+ return [[self alloc] initWithType:CSSAttributeSelectorExactMatch attributeName:@"id" attrbiuteValue:elementId];
+}
+
++ (instancetype)hasAttributeSelector:(NSString *)attributeName
+{
+ return [[self alloc] initWithType:CSSAttributeSelectorExists attributeName:attributeName attrbiuteValue:@""];
+}
+
+- (instancetype)initWithType:(CSSAttributeSelectorType)type
+ attributeName:(NSString *)name
+ attrbiuteValue:(NSString *)value
+{
+ self = [super init];
+ if (self) {
+ self.type = type;
+ _name = [name copy];
+ _value = value ? [value copy]: @"";
+ }
+ return self;
+}
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ switch (_type) {
+ case CSSAttributeSelectorExists:
+ {
+ return !!element[_name];
+ }
+ case CSSAttributeSelectorExactMatch:
+ {
+ return [element[_name] isEqualToString:_value];
+ }
+ case CSSAttributeSelectorIncludes:
+ {
+ NSArray *components = [element[_name] componentsSeparatedByCharactersInSet:[NSCharacterSet HTMLWhitespaceCharacterSet]];
+ return [components containsObject:_value];
+ }
+ case CSSAttributeSelectorBegins:
+ {
+ return [element[_name] hasPrefix:_value];
+ }
+ case CSSAttributeSelectorEnds:
+ {
+ return [element[_name] hasSuffix:_value];
+ }
+ case CSSAttributeSelectorContains:
+ {
+ return [element[_name] containsString:_value];
+ }
+ case CSSAttributeSelectorHyphen:
+ {
+ return [element[_name] isEqualToString:_value] || [element[_name] hasPrefix:[_value stringByAppendingString:@"-"]];
+ }
+ case CSSAttributeSelectorNot:
+ {
+ return ![element[_name] isEqualToString:_value];
+ }
+ default:
+ return NO;
+ }
+}
+
+#pragma mark - Description
+
+- (NSString *)debugDescription
+{
+ if (self.type == CSSAttributeSelectorExists) {
+ return [NSString stringWithFormat:@"[%@]", self.name];
+ }
+
+ NSString *matcher = @{@(CSSAttributeSelectorExactMatch): @"=",
+ @(CSSAttributeSelectorIncludes): @"~=",
+ @(CSSAttributeSelectorBegins): @"^=",
+ @(CSSAttributeSelectorEnds): @"$=",
+ @(CSSAttributeSelectorContains): @"*=",
+ @(CSSAttributeSelectorHyphen): @"|=",
+ @(CSSAttributeSelectorNot): @"!="}[@(self.type)];
+
+ return [NSString stringWithFormat:@"[%@%@'%@']", self.name, matcher, self.value];
+}
+
+@end
diff --git a/HTMLKit/CSSCodePoints.h b/HTMLKit/CSSCodePoints.h
new file mode 100644
index 0000000..0d1a0fc
--- /dev/null
+++ b/HTMLKit/CSSCodePoints.h
@@ -0,0 +1,140 @@
+//
+// CSSTokenizer CODEPOINTs.h
+// HTMLKit
+//
+// Created by Iska on 08/06/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#define CODEPOINTS \
+ CODEPOINT( CONTROL, 0x0080 ) \
+ CODEPOINT( CHARACTER_TABULATION, 0x0009 ) \
+ CODEPOINT( LINE_FEED, 0x000A ) \
+ CODEPOINT( SPACE, 0x0020 ) \
+ CODEPOINT( QUOTATION_MARK, 0x0022 ) \
+ CODEPOINT( NUMBER_SIGN, 0x0023 ) \
+ CODEPOINT( DOLLAR_SIGN, 0x0024 ) \
+ CODEPOINT( APOSTROPHE, 0x0027 ) \
+ CODEPOINT( LEFT_PARENTHESIS, 0x0028 ) \
+ CODEPOINT( RIGHT_PARENTHESIS, 0x0029 ) \
+ CODEPOINT( ASTERIX, 0x002A ) \
+ CODEPOINT( PLUS_SIGN, 0x002B ) \
+ CODEPOINT( COMMA, 0x002C ) \
+ CODEPOINT( HYPHEN_MINUS, 0x002D ) \
+ CODEPOINT( FULL_STOP, 0x002E ) \
+ CODEPOINT( DIGIT_ZERO, 0x0030 ) \
+ CODEPOINT( DIGIT_NINE, 0x0039 ) \
+ CODEPOINT( COLON, 0x003A ) \
+ CODEPOINT( EQUALS_SIGN, 0x003D ) \
+ CODEPOINT( GREATER_THAN_SIGN, 0x003E ) \
+ CODEPOINT( LATIN_CAPITAL_LETTER_A, 0x0041 ) \
+ CODEPOINT( LATIN_CAPITAL_LETTER_F, 0x0046 ) \
+ CODEPOINT( LATIN_CAPITAL_LETTER_Z, 0x005A ) \
+ CODEPOINT( LEFT_SQUARE_BRACKET, 0x005B ) \
+ CODEPOINT( REVERSE_SOLIDUS, 0x005C ) \
+ CODEPOINT( RIGHT_SQUARE_BRACKET, 0x005D ) \
+ CODEPOINT( CIRCUMFLEX_ACCENT, 0x005E ) \
+ CODEPOINT( LOW_LINE, 0x005F ) \
+ CODEPOINT( LATIN_SMALL_LETTER_A, 0x0061 ) \
+ CODEPOINT( LATIN_SMALL_LETTER_F, 0x0066 ) \
+ CODEPOINT( LATIN_SMALL_LETTER_Z, 0x007A ) \
+ CODEPOINT( VERTICAL_LINE, 0x007C ) \
+ CODEPOINT( TILDE, 0x007E ) \
+ CODEPOINT( REPLACEMENT_CHARACTER, 0xFFFD )
+
+#define CODEPOINT( name, value ) static UniChar const name = value;
+CODEPOINTS
+#undef CODEPOINT
+
+NS_INLINE BOOL isWhitespace(UTF32Char codePoint)
+{
+ return (codePoint == CHARACTER_TABULATION ||
+ codePoint == LINE_FEED ||
+ codePoint == SPACE);
+}
+
+NS_INLINE BOOL isCombinator(UTF32Char codePoint)
+{
+ return (codePoint == SPACE ||
+ codePoint == PLUS_SIGN ||
+ codePoint == COMMA ||
+ codePoint == GREATER_THAN_SIGN ||
+ codePoint == TILDE);
+}
+
+NS_INLINE BOOL isDigit(UTF32Char codePoint)
+{
+ return codePoint >= DIGIT_ZERO && codePoint <= DIGIT_NINE;
+}
+
+NS_INLINE BOOL isHexDigit(UTF32Char codePoint)
+{
+ return ((codePoint >= DIGIT_ZERO && codePoint <= DIGIT_NINE) ||
+ (codePoint >= LATIN_CAPITAL_LETTER_A && codePoint <= LATIN_CAPITAL_LETTER_F) ||
+ (codePoint >= LATIN_SMALL_LETTER_A && codePoint <= LATIN_SMALL_LETTER_F));
+}
+
+NS_INLINE BOOL isQuote(UTF32Char codePoint)
+{
+ return codePoint == QUOTATION_MARK || codePoint == APOSTROPHE;
+}
+
+NS_INLINE BOOL isNewLine(UTF32Char codePoint)
+{
+ return codePoint == LINE_FEED;
+}
+
+NS_INLINE BOOL isNameStart(UTF32Char codePoint)
+{
+ return ((codePoint >= LATIN_CAPITAL_LETTER_A && codePoint <= LATIN_CAPITAL_LETTER_Z) ||
+ (codePoint >= LATIN_SMALL_LETTER_A && codePoint <= LATIN_SMALL_LETTER_Z) ||
+ codePoint >= CONTROL ||
+ codePoint == LOW_LINE);
+}
+
+NS_INLINE BOOL isName(UTF32Char codePoint)
+{
+ return isNameStart(codePoint) || isDigit(codePoint) || codePoint == HYPHEN_MINUS;
+}
+
+NS_INLINE BOOL isValidEscape(UTF32Char first, UTF32Char second)
+{
+ if (first != REVERSE_SOLIDUS || isNewLine(second)) {
+ return false;
+ }
+
+ return YES;
+}
+
+NS_INLINE BOOL isValidEscapedCodePoint(UTF32Char codePoint)
+{
+ return (codePoint != 0 &&
+ !(codePoint >= 0xD800 && codePoint <= 0x0DFFF) &&
+ codePoint <= 0x10FFFF);
+}
+
+NS_INLINE BOOL isValidIdentifierStart(UTF32Char first, UTF32Char second, UTF32Char third)
+{
+ if (first == HYPHEN_MINUS) {
+ if (isNameStart(second) ||
+ second == HYPHEN_MINUS ||
+ isValidEscape(second, third)) {
+ return YES;
+ } else {
+ return NO;
+ }
+ } else if (isNameStart(first)) {
+ return YES;
+ } else if (first == REVERSE_SOLIDUS) {
+ return isValidEscape(first, second);
+ } else {
+ return NO;
+ }
+}
+
+NS_INLINE void AppendCodePoint(CFMutableStringRef string, UTF32Char codePoint)
+{
+ UniChar pair[2];
+ Boolean isPair = CFStringGetSurrogatePairForLongCharacter(codePoint, pair);
+ CFStringAppendCharacters(string, pair, isPair ? 2 : 1);
+}
diff --git a/HTMLKit/CSSCombinatorSelector.h b/HTMLKit/CSSCombinatorSelector.h
new file mode 100644
index 0000000..b227736
--- /dev/null
+++ b/HTMLKit/CSSCombinatorSelector.h
@@ -0,0 +1,22 @@
+//
+// CSSCombinatorSelector.h
+// HTMLKit
+//
+// Created by Iska on 12/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelector.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CSSCombinatorSelector : CSSSelector
+
++ (instancetype)childOfElementCombinator:(CSSSelector *)selector;
++ (instancetype)descendantOfElementCombinator:(CSSSelector *)selector;
++ (instancetype)adjacentSiblingCombinator:(CSSSelector *)selector;
++ (instancetype)generalSiblingCombinator:(CSSSelector *)selector;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSCombinatorSelector.m b/HTMLKit/CSSCombinatorSelector.m
new file mode 100644
index 0000000..847ae7f
--- /dev/null
+++ b/HTMLKit/CSSCombinatorSelector.m
@@ -0,0 +1,154 @@
+//
+// CSSCombinatorSelector.m
+// HTMLKit
+//
+// Created by Iska on 12/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSCombinatorSelector.h"
+#import "HTMLElement.h"
+
+#pragma mark - Declarations
+
+@interface CSSChildOfElementCombinatorSelector : CSSCombinatorSelector
+@end
+
+@interface CSSDecendantOfElementCombinatorSelector : CSSCombinatorSelector
+@end
+
+@interface CSSAdjacentSiblingCombinatorSelector : CSSCombinatorSelector
+@end
+
+@interface CSSGeneralSiblingCombinatorSelector : CSSCombinatorSelector
+@end
+
+#pragma mark - Base Combinator
+
+@interface CSSCombinatorSelector ()
+{
+ CSSSelector *_selector;
+}
+@property (nonatomic, strong, readonly) CSSSelector *selector;
+@end
+
+@implementation CSSCombinatorSelector
+@synthesize selector = _selector;
+
++ (instancetype)childOfElementCombinator:(CSSSelector *)selector
+{
+ return [[CSSChildOfElementCombinatorSelector alloc] initWithSelector:selector];
+}
+
++ (instancetype)descendantOfElementCombinator:(CSSSelector *)selector
+{
+ return [[CSSDecendantOfElementCombinatorSelector alloc] initWithSelector:selector];
+}
+
++ (instancetype)adjacentSiblingCombinator:(CSSSelector *)selector
+{
+ return [[CSSAdjacentSiblingCombinatorSelector alloc] initWithSelector:selector];
+}
+
++ (instancetype)generalSiblingCombinator:(CSSSelector *)selector
+{
+ return [[CSSGeneralSiblingCombinatorSelector alloc] initWithSelector:selector];
+}
+
+- (instancetype)initWithSelector:(CSSSelector *)selector
+{
+ self = [super init];
+ if (self) {
+ _selector = selector;
+ }
+ return self;
+}
+
+@end
+
+#pragma mark - Child OfElement Combinator
+
+@implementation CSSChildOfElementCombinatorSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ HTMLElement *parent = element.parentElement;
+ return parent != nil && [self.selector acceptElement:parent];
+}
+
+- (NSString *)debugDescription
+{
+ return [NSString stringWithFormat:@"%@ > ", self.selector.debugDescription];
+}
+
+@end
+
+#pragma mark - Decendant Of Element Combinator
+
+@implementation CSSDecendantOfElementCombinatorSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ HTMLElement *parent = element.parentElement;
+
+ while (parent != nil) {
+ if ([self.selector acceptElement:parent]) {
+ return YES;
+ }
+ parent = parent.parentElement;
+ }
+
+ return NO;
+}
+
+- (NSString *)debugDescription
+{
+ return [NSString stringWithFormat:@"%@ ", self.selector.debugDescription];
+}
+
+@end
+
+#pragma mark - Adjacent Sibling Combinator
+
+@implementation CSSAdjacentSiblingCombinatorSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ HTMLNode *previous = element.previousSiblingElement;
+ if (previous == nil || previous.nodeType != HTMLNodeElement) {
+ return NO;
+ }
+ return [self.selector acceptElement:previous.asElement];
+}
+
+- (NSString *)debugDescription
+{
+ return [NSString stringWithFormat:@"%@ + ", self.selector.debugDescription];
+}
+
+@end
+
+#pragma mark - General Sibling Combinator
+
+@implementation CSSGeneralSiblingCombinatorSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ HTMLNode *previous = element.previousSiblingElement;
+
+ while (previous != nil && previous.nodeType == HTMLNodeElement) {
+ if ([self.selector acceptElement:previous.asElement]) {
+ return YES;
+ }
+ previous = previous.previousSiblingElement;
+ }
+
+ return NO;
+}
+
+- (NSString *)debugDescription
+{
+ return [NSString stringWithFormat:@"%@ ~ ", self.selector.debugDescription];
+}
+
+@end
diff --git a/HTMLKit/CSSCompoundSelector.h b/HTMLKit/CSSCompoundSelector.h
new file mode 100644
index 0000000..00873d3
--- /dev/null
+++ b/HTMLKit/CSSCompoundSelector.h
@@ -0,0 +1,22 @@
+//
+// CSSCompoundSelector.h
+// HTMLKit
+//
+// Created by Iska on 18/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelector.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CSSCompoundSelector : CSSSelector
+
++ (instancetype)andSelector:(NSArray *)selectors;
++ (instancetype)orSelector:(NSArray *)selectors;
+
+- (void)addSelector:(CSSSelector *)selector;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSCompoundSelector.m b/HTMLKit/CSSCompoundSelector.m
new file mode 100644
index 0000000..712d55f
--- /dev/null
+++ b/HTMLKit/CSSCompoundSelector.m
@@ -0,0 +1,99 @@
+//
+// CSSCompoundSelector.m
+// HTMLKit
+//
+// Created by Iska on 18/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSCompoundSelector.h"
+
+#pragma mark - Declarations
+
+@interface CSSAndCompoundSelector : CSSCompoundSelector
+@end
+
+@interface CSSOrCompoundSelector : CSSCompoundSelector
+@end
+
+#pragma mark - Base Combinator
+
+@interface CSSCompoundSelector ()
+{
+ NSMutableArray *_selectors;
+}
+@property (nonatomic, strong, readonly) NSArray *selectors;
+@end
+
+@implementation CSSCompoundSelector
+@synthesize selectors = _selectors;
+
++ (instancetype)andSelector:(NSArray *)selectors
+{
+ return [[CSSAndCompoundSelector alloc] initWithSelectors:selectors];
+}
+
++ (instancetype)orSelector:(NSArray *)selectors
+{
+ return [[CSSOrCompoundSelector alloc] initWithSelectors:selectors];
+}
+
+- (instancetype)initWithSelectors:(NSArray *)selectors
+{
+ self = [super init];
+ if (self) {
+ _selectors = [[NSMutableArray alloc] initWithArray:selectors];
+ }
+ return self;
+}
+
+- (void)addSelector:(CSSSelector *)selector
+{
+ [_selectors addObject:selector];
+}
+
+@end
+
+#pragma mark - And Compound Selector
+
+@implementation CSSAndCompoundSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ for (CSSSelector *selector in self.selectors) {
+ if (![selector acceptElement:element]) {
+ return NO;
+ }
+ }
+ return YES;
+}
+
+- (NSString *)debugDescription
+{
+ NSArray *descriptions = [self.selectors valueForKey:@"debugDescription"];
+ return [descriptions componentsJoinedByString:@""];
+}
+
+@end
+
+#pragma mark - Or Compound Selector
+
+@implementation CSSOrCompoundSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ for (CSSSelector *selector in self.selectors) {
+ if ([selector acceptElement:element]) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (NSString *)debugDescription
+{
+ NSArray *descriptions = [self.selectors valueForKey:@"debugDescription"];
+ return [descriptions componentsJoinedByString:@","];
+}
+
+@end
diff --git a/HTMLKit/CSSInputStream.h b/HTMLKit/CSSInputStream.h
new file mode 100644
index 0000000..3bf9c1e
--- /dev/null
+++ b/HTMLKit/CSSInputStream.h
@@ -0,0 +1,20 @@
+//
+// CSSInputStream.h
+// HTMLKit
+//
+// Created by Iska on 07/06/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "HTMLInputStreamReader.h"
+
+@interface CSSInputStream : HTMLInputStreamReader
+
+- (void)consumeWhitespace;
+- (NSString *)consumeIdentifier;
+- (NSString *)consumeStringWithEndingCodePoint:(UTF32Char)endingCodePoint;
+- (UTF32Char)consumeEscapedCodePoint;
+- (NSString *)consumeCombinator;
+
+@end
diff --git a/HTMLKit/CSSInputStream.m b/HTMLKit/CSSInputStream.m
new file mode 100644
index 0000000..f9336be
--- /dev/null
+++ b/HTMLKit/CSSInputStream.m
@@ -0,0 +1,128 @@
+//
+// CSSInputStream.m
+// HTMLKit
+//
+// Created by Iska on 07/06/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSInputStream.h"
+#import "CSSCodePoints.h"
+
+@interface HTMLInputStreamReader ()
+- (void)emitParseError:(NSString *)reason;
+@end
+
+@implementation CSSInputStream
+
+- (void)consumeWhitespace
+{
+ while (isWhitespace(self.nextInputCharacter)) {
+ [self consumeNextInputCharacter];
+ }
+}
+
+- (NSString *)consumeIdentifier
+{
+ CFMutableStringRef value = CFStringCreateMutable(kCFAllocatorDefault, 0);
+
+ if (!isValidIdentifierStart([self inputCharacterPointAtOffset:0],
+ [self inputCharacterPointAtOffset:1],
+ [self inputCharacterPointAtOffset:2])) {
+ return nil;
+ }
+
+ while (YES) {
+ UTF32Char codePoint = [self consumeNextInputCharacter];
+ if (codePoint == EOF) {
+ break;
+ } else if (isName(codePoint)) {
+ AppendCodePoint(value, codePoint);
+ } else if (isValidEscape(codePoint, [self inputCharacterPointAtOffset:1])) {
+ UTF32Char escapedCodePoint = [self consumeEscapedCodePoint];
+ AppendCodePoint(value, escapedCodePoint);
+ } else {
+ [self reconsumeCurrentInputCharacter];
+ break;
+ }
+ }
+
+ return (__bridge NSString *)(CFStringGetLength(value) > 0 ? value : nil);
+}
+
+- (NSString *)consumeStringWithEndingCodePoint:(UTF32Char)endingCodePoint
+{
+ CFMutableStringRef value = CFStringCreateMutable(kCFAllocatorDefault, 0);
+
+ while (YES) {
+ UTF32Char codePoint = [self consumeNextInputCharacter];
+ if (codePoint == endingCodePoint) {
+ break;
+ }
+
+ switch (codePoint) {
+ case EOF:
+ break;
+ case LINE_FEED:
+ [self emitParseError:@"New-line character (0x000A) in CSS attribute value"];
+ [self reconsumeCurrentInputCharacter];
+ break;
+ case REVERSE_SOLIDUS:
+ {
+ UTF32Char next = self.nextInputCharacter;
+ if (next == EOF) {
+ continue;
+ } else if (next == LINE_FEED) {
+ [self consumeNextInputCharacter];
+ } else {
+ UTF32Char escapedCodePoint = [self consumeNextInputCharacter];
+ AppendCodePoint(value, escapedCodePoint);
+ }
+ }
+ default:
+ AppendCodePoint(value, codePoint);
+ break;
+ }
+ }
+
+ return (__bridge NSString *)(CFStringGetLength(value) > 0 ? value : nil);
+}
+
+- (UTF32Char)consumeEscapedCodePoint
+{
+ UTF32Char codePoint = [self consumeNextInputCharacter];
+
+ if (isHexDigit(codePoint)) {
+ CFMutableStringRef hexString = CFStringCreateMutable(kCFAllocatorDefault, 6);
+ AppendCodePoint(hexString, codePoint);
+
+ while (isHexDigit(self.nextInputCharacter) && CFStringGetLength(hexString) <= 6) {
+ UniChar codePoint = [self consumeNextInputCharacter];
+ CFStringAppendCharacters(hexString, &codePoint, 1);
+ }
+
+ if (isWhitespace(self.nextInputCharacter)) {
+ [self consumeNextInputCharacter];
+ }
+
+ NSScanner *scanner = [NSScanner scannerWithString:(__bridge NSString *)(hexString)];
+ UTF32Char number;
+ [scanner scanHexInt:&number];
+
+ return isValidEscapedCodePoint(number) ? number : REPLACEMENT_CHARACTER;
+ } else if (codePoint == EOF) {
+ return REPLACEMENT_CHARACTER;
+ }
+
+ return codePoint;
+}
+
+- (NSString *)consumeCombinator
+{
+ NSString *combinator = [self consumeCharactersInString:@" >+~"];
+ combinator = [combinator stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ return combinator;
+}
+
+@end
diff --git a/HTMLKit/CSSNthExpressionParser.h b/HTMLKit/CSSNthExpressionParser.h
new file mode 100644
index 0000000..e484bab
--- /dev/null
+++ b/HTMLKit/CSSNthExpressionParser.h
@@ -0,0 +1,16 @@
+//
+// CSSNthExpression.h
+// HTMLKit
+//
+// Created by Iska on 10/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSSelectors.h"
+
+@interface CSSNthExpressionParser : NSObject
+
++ (CSSNthExpression)parseExpression:(NSString *)expression;
+
+@end
diff --git a/HTMLKit/CSSNthExpressionParser.m b/HTMLKit/CSSNthExpressionParser.m
new file mode 100644
index 0000000..392acb4
--- /dev/null
+++ b/HTMLKit/CSSNthExpressionParser.m
@@ -0,0 +1,50 @@
+//
+// CSSNthExpression.m
+// HTMLKit
+//
+// Created by Iska on 10/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSNthExpressionParser.h"
+#import "CSSCodePoints.h"
+#import "NSString+HTMLKit.h"
+#import "NSCharacterSet+HTMLKit.h"
+
+@implementation CSSNthExpressionParser
+
++ (CSSNthExpression)parseExpression:(NSString *)expression
+{
+ NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+
+ NSString *string = [expression.lowercaseString copy];
+ string = [[string stringByTrimmingCharactersInSet:whitespace] copy];
+
+ if ([string isEqualToStringIgnoringCase:@"odd"]) {
+ return CSSNthExpressionOdd;
+ } else if ([string isEqualToStringIgnoringCase:@"even"]) {
+ return CSSNthExpressionEven;
+ }
+
+ NSCharacterSet *set = [[NSCharacterSet CSSNthExpressionCharacterSet] invertedSet];
+ if ([string rangeOfCharacterFromSet:set].location != NSNotFound) {
+ return CSSNthExpressionMake(0, 0);
+ }
+ NSArray *parts = [string componentsSeparatedByString:@"n"];
+
+ if (parts.count == 1) {
+ NSInteger b = [parts[0] integerValue];
+ return CSSNthExpressionMake(0, b);
+ } else if (parts.count == 2) {
+ NSInteger a = [parts[0] integerValue];
+ if (a == 0) {
+ a = [parts[0] isEqualToString:@"-"] ? -1 : 1;
+ }
+ NSInteger b = [parts[1] integerValue];
+ return CSSNthExpressionMake(a, b);
+ } else {
+ return CSSNthExpressionMake(0, 0);
+ }
+}
+
+@end
diff --git a/HTMLKit/CSSNthExpressionSelector.h b/HTMLKit/CSSNthExpressionSelector.h
new file mode 100644
index 0000000..f8ddccf
--- /dev/null
+++ b/HTMLKit/CSSNthExpressionSelector.h
@@ -0,0 +1,25 @@
+//
+// CSSNthExpressionSelector.h
+// HTMLKit
+//
+// Created by Iska on 10/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelector.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CSSNthExpressionSelector : CSSSelector
+
+@property (nonatomic, strong, readonly) NSString *className;
+@property (nonatomic, assign, readonly) CSSNthExpression expression;
+
++ (instancetype)nthChildSelector:(CSSNthExpression)expression;
++ (instancetype)nthLastChildSelector:(CSSNthExpression)expression;
++ (instancetype)nthOfTypeSelector:(CSSNthExpression)expression;
++ (instancetype)nthLastOfTypeSelector:(CSSNthExpression)expression;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSNthExpressionSelector.m b/HTMLKit/CSSNthExpressionSelector.m
new file mode 100644
index 0000000..0ef6e5e
--- /dev/null
+++ b/HTMLKit/CSSNthExpressionSelector.m
@@ -0,0 +1,130 @@
+//
+// CSSNthExpressionSelector.m
+// HTMLKit
+//
+// Created by Iska on 10/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSNthExpressionSelector.h"
+#import "HTMLElement.h"
+
+#pragma mark - Nth-Expression
+
+const CSSNthExpression CSSNthExpressionOdd = (CSSNthExpression) {
+ .an = 2, .b = 1
+};
+
+const CSSNthExpression CSSNthExpressionEven = (CSSNthExpression) {
+ .an = 2, .b = 0
+};
+
+NSString * _Nonnull NSStringFromNthExpression(CSSNthExpression expression)
+{
+ if (expression.an == 0 && expression.b == 0) {
+ return @"invalid";
+ }
+
+ if (expression.an == 0) {
+ return [NSString stringWithFormat:@"%ld", expression.b];
+ }
+ if (expression.b == 0) {
+ return [NSString stringWithFormat:@"%ldn", expression.an];
+ }
+
+ return [NSString stringWithFormat:@"%ldn%+ld", expression.an, expression.b];
+}
+
+#pragma mark - Implementation
+
+NSInteger computeIndex(NSEnumerator *enumerator, HTMLElement *element)
+{
+ NSInteger index = 0;
+ for (HTMLNode *node in enumerator) {
+ if (node.nodeType != HTMLNodeElement) {
+ continue;
+ }
+
+ if ([node.asElement.tagName isEqualToString:element.tagName]) {
+ index++;
+ }
+
+ if (node == element) {
+ break;
+ }
+ }
+
+ return index;
+}
+
+@interface CSSNthExpressionSelector ()
+{
+ NSString *_className;
+ CSSNthExpression _expression;
+ NSInteger (^ _computeIndex)(HTMLElement *);
+}
+@end
+
+@implementation CSSNthExpressionSelector
+@synthesize expression = _expression;
+@synthesize className = _className;
+
++ (instancetype)nthChildSelector:(CSSNthExpression)expression
+{
+ return [[self alloc] initWithClassName:@"nth-child" expression:expression block:^NSInteger(HTMLElement *element) {
+ return [element.parentElement indexOfChildElement:element] + 1;
+ }];
+}
+
++ (instancetype)nthLastChildSelector:(CSSNthExpression)expression
+{
+ return [[self alloc] initWithClassName:@"nth-last-child" expression:expression block:^NSInteger(HTMLElement *element) {
+ return element.parentElement.childElementsCount - [element.parentElement indexOfChildElement:element];
+ }];
+}
+
++ (instancetype)nthOfTypeSelector:(CSSNthExpression)expression
+{
+ return [[self alloc] initWithClassName:@"nth-of-type" expression:expression block:^NSInteger(HTMLElement *element) {
+ return computeIndex(element.parentElement.childNodes.array.objectEnumerator, element);
+ }];
+}
+
++ (instancetype)nthLastOfTypeSelector:(CSSNthExpression)expression
+{
+ return [[self alloc] initWithClassName:@"nth-last-of-type" expression:expression block:^NSInteger(HTMLElement *element) {
+ return computeIndex(element.parentElement.childNodes.array.reverseObjectEnumerator, element);
+ }];
+}
+
+- (instancetype)initWithClassName:(NSString *)className
+ expression:(CSSNthExpression)expression
+ block:(NSInteger (^)(HTMLElement *element))block
+{
+ self = [super init];
+ if (self) {
+ _className = [className copy];
+ _expression = expression;
+ _computeIndex = [block copy];
+ }
+ return self;
+}
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ NSInteger index = _computeIndex(element);
+
+ if (_expression.an == 0) {
+ return index == _expression.b;
+ } else {
+ NSInteger diff = (index - _expression.b);
+ return (diff * _expression.an >= 0) && (diff % _expression.an == 0);
+ }
+}
+
+- (NSString *)debugDescription
+{
+ return [NSString stringWithFormat:@":%@(%@)", self.className, NSStringFromNthExpression(self.expression)];
+}
+
+@end
diff --git a/HTMLKit/CSSPseudoClassSelector.h b/HTMLKit/CSSPseudoClassSelector.h
new file mode 100644
index 0000000..7f15cd9
--- /dev/null
+++ b/HTMLKit/CSSPseudoClassSelector.h
@@ -0,0 +1,21 @@
+//
+// CSSPseudoClassSelector.h
+// HTMLKit
+//
+// Created by Iska on 06/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelector.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CSSPseudoClassSelector : CSSSelector
+
+@property (nonatomic, strong, readonly) NSString *className;
+
+- (instancetype)initWithClassName:(NSString *)className selector:(CSSSelector *)selector;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSPseudoClassSelector.m b/HTMLKit/CSSPseudoClassSelector.m
new file mode 100644
index 0000000..3471670
--- /dev/null
+++ b/HTMLKit/CSSPseudoClassSelector.m
@@ -0,0 +1,46 @@
+//
+// CSSPseudoClassSelector.m
+// HTMLKit
+//
+// Created by Iska on 06/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSPseudoClassSelector.h"
+
+@interface CSSPseudoClassSelector ()
+{
+ NSString *_className;
+ CSSSelector *_selector;
+}
+@end
+
+@implementation CSSPseudoClassSelector
+@synthesize className = _className;
+
+- (instancetype)initWithClassName:(NSString *)className selector:(CSSSelector *)selector
+{
+ self = [super init];
+ if (self) {
+ _className = [className copy];
+ _selector = selector;
+ }
+ return self;
+}
+
+-(BOOL)acceptElement:(HTMLElement *)element
+{
+ return [_selector acceptElement:element];
+}
+
+- (NSString *)debugDescription
+{
+ return [NSString stringWithFormat:@":%@", self.className];
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p '%@'>", self.class, self, self.debugDescription];
+}
+
+@end
diff --git a/HTMLKit/CSSPseudoFunctionSelector.h b/HTMLKit/CSSPseudoFunctionSelector.h
new file mode 100644
index 0000000..d5d69f3
--- /dev/null
+++ b/HTMLKit/CSSPseudoFunctionSelector.h
@@ -0,0 +1,20 @@
+//
+// CSSPseudoFunctionSelector.h
+// HTMLKit
+//
+// Created by Iska on 07/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelector.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CSSPseudoFunctionSelector : CSSSelector
+
++ (instancetype)notSelector:(CSSSelector *)selector;
++ (instancetype)hasSelector:(CSSSelector *)selector;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSPseudoFunctionSelector.m b/HTMLKit/CSSPseudoFunctionSelector.m
new file mode 100644
index 0000000..9c22407
--- /dev/null
+++ b/HTMLKit/CSSPseudoFunctionSelector.m
@@ -0,0 +1,89 @@
+//
+// CSSPseudoFunctionSelector.m
+// HTMLKit
+//
+// Created by Iska on 07/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSPseudoFunctionSelector.h"
+#import "HTMLElement.h"
+
+#pragma mark - Declarations
+
+@interface CSSNotSelector : CSSPseudoFunctionSelector
+@end
+
+@interface CSSHasSelector : CSSPseudoFunctionSelector
+@end
+
+#pragma mark - Base Function Selector
+
+@interface CSSPseudoFunctionSelector ()
+{
+ CSSSelector *_selector;
+}
+@property (nonatomic, strong, readonly) CSSSelector *selector;
+@end
+
+@implementation CSSPseudoFunctionSelector
+@synthesize selector = _selector;
+
++ (instancetype)notSelector:(CSSSelector *)selector
+{
+ return [[CSSNotSelector alloc] initWithSelector:selector];
+}
+
++ (instancetype)hasSelector:(CSSSelector *)selector
+{
+ return [[CSSHasSelector alloc] initWithSelector:selector];
+}
+
+- (instancetype)initWithSelector:(CSSSelector *)selector
+{
+ self = [super init];
+ if (self) {
+ _selector = selector;
+ }
+ return self;
+}
+
+@end
+
+#pragma mark - Not Selector
+
+@implementation CSSNotSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ return ![self.selector acceptElement:element];
+}
+
+- (NSString *)debugDescription
+{
+ return [NSString stringWithFormat:@":not(%@)", self.selector.debugDescription];
+}
+
+@end
+
+#pragma mark - Has Selector
+
+@implementation CSSHasSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ for (HTMLNode *child in element.childNodes) {
+ if (child.nodeType == HTMLNodeElement && [self.selector acceptElement:child.asElement]) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+- (NSString *)debugDescription
+{
+ return [NSString stringWithFormat:@":has(%@)", self.selector.debugDescription];
+}
+
+@end
diff --git a/HTMLKit/CSSSelector.h b/HTMLKit/CSSSelector.h
new file mode 100644
index 0000000..d13fb58
--- /dev/null
+++ b/HTMLKit/CSSSelector.h
@@ -0,0 +1,49 @@
+//
+// HTMLSelector.h
+// HTMLKit
+//
+// Created by Iska on 02/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+
+#pragma mark - Attribute Selector Type
+
+typedef NS_ENUM(NSUInteger, CSSAttributeSelectorType)
+{
+ CSSAttributeSelectorExists,
+ CSSAttributeSelectorExactMatch,
+ CSSAttributeSelectorIncludes,
+ CSSAttributeSelectorBegins,
+ CSSAttributeSelectorEnds,
+ CSSAttributeSelectorContains,
+ CSSAttributeSelectorHyphen,
+ CSSAttributeSelectorNot
+};
+
+#pragma mark - CSS Nth-Expression
+
+typedef struct CSSNthExpression
+{
+ NSInteger an;
+ NSInteger b;
+} CSSNthExpression;
+
+NS_INLINE CSSNthExpression CSSNthExpressionMake(NSInteger an, NSInteger b) {
+ return (CSSNthExpression){ .an = an, .b = b };
+}
+
+extern const CSSNthExpression CSSNthExpressionOdd;
+extern const CSSNthExpression CSSNthExpressionEven;
+extern NSString * _Nonnull NSStringFromNthExpression(CSSNthExpression expression);
+
+#pragma mark - Base Selector Class
+
+@class HTMLElement;
+
+@interface CSSSelector : NSObject
+
+- (BOOL)acceptElement:(nonnull HTMLElement *)element;
+
+@end
diff --git a/HTMLKit/CSSSelector.m b/HTMLKit/CSSSelector.m
new file mode 100644
index 0000000..f3a8e52
--- /dev/null
+++ b/HTMLKit/CSSSelector.m
@@ -0,0 +1,33 @@
+//
+// CSSSelector.m
+// HTMLKit
+//
+// Created by Iska on 15/10/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSSelector.h"
+
+@implementation CSSSelector
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ [self doesNotRecognizeSelector:_cmd];
+ return NO;
+}
+
+#pragma mark - Description
+
+- (NSString *)debugDescription
+{
+ [self doesNotRecognizeSelector:_cmd];
+ return @"";
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p '%@'>", self.class, self, self.debugDescription];
+}
+
+@end
diff --git a/HTMLKit/CSSSelectorBlock.h b/HTMLKit/CSSSelectorBlock.h
new file mode 100644
index 0000000..f1030e7
--- /dev/null
+++ b/HTMLKit/CSSSelectorBlock.h
@@ -0,0 +1,21 @@
+//
+// CSSSelectorBlock.h
+// HTMLKit
+//
+// Created by Iska on 20/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelector.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class HTMLElement;
+
+@interface CSSSelectorBlock : CSSSelector
+
+- (instancetype)initWithName:(NSString *)name block:(BOOL (^)(HTMLElement *))block;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSSelectorBlock.m b/HTMLKit/CSSSelectorBlock.m
new file mode 100644
index 0000000..c8d44d6
--- /dev/null
+++ b/HTMLKit/CSSSelectorBlock.m
@@ -0,0 +1,40 @@
+//
+// CSSSelectorBlock.m
+// HTMLKit
+//
+// Created by Iska on 20/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelectorBlock.h"
+
+@interface CSSSelectorBlock ()
+{
+ NSString *_name;
+ BOOL (^ _acceptBlock)(HTMLElement *);
+}
+@end
+
+@implementation CSSSelectorBlock
+
+- (instancetype)initWithName:(NSString *)name block:(BOOL (^)(HTMLElement *))block
+{
+ self = [super init];
+ if (self) {
+ _name = [name copy];
+ _acceptBlock = [block copy];
+ }
+ return self;
+}
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ return _acceptBlock ? _acceptBlock(element) : NO;
+}
+
+- (NSString *)debugDescription
+{
+ return _name;
+}
+
+@end
diff --git a/HTMLKit/CSSSelectorParser.h b/HTMLKit/CSSSelectorParser.h
new file mode 100644
index 0000000..1d55788
--- /dev/null
+++ b/HTMLKit/CSSSelectorParser.h
@@ -0,0 +1,17 @@
+//
+// CSSSelectorParser.h
+// HTMLKit
+//
+// Created by Iska on 02/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import
+
+@class CSSSelector;
+
+@interface CSSSelectorParser : NSObject
+
++ (CSSSelector *)parseSelector:(NSString *)string error:(NSError **)error;
+
+@end
diff --git a/HTMLKit/CSSSelectorParser.m b/HTMLKit/CSSSelectorParser.m
new file mode 100644
index 0000000..c550351
--- /dev/null
+++ b/HTMLKit/CSSSelectorParser.m
@@ -0,0 +1,367 @@
+//
+// CSSSelectorParser.m
+// HTMLKit
+//
+// Created by Iska on 02/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelectorParser.h"
+#import "CSSInputStream.h"
+#import "CSSCodePoints.h"
+#import "CSSSelectors.h"
+#import "NSString+HTMLKit.h"
+#import "NSCharacterSet+HTMLKit.h"
+#import "CSSNthExpressionParser.h"
+#import "CSSCompoundSelector.h"
+#import "HTMLKitErrorDomain.h"
+
+@interface CSSSelectorParser ()
+{
+ NSString *_string;
+ CSSInputStream *_inputStream;
+ NSUInteger _location;
+
+ NSMutableArray *_selectors;
+}
+@end
+
+@implementation CSSSelectorParser
+
++ (CSSSelector *)parseSelector:(NSString *)string error:(NSError * __autoreleasing *)error
+{
+ CSSSelectorParser *parser = [[CSSSelectorParser alloc] initWithString:string];
+ CSSSelector *selector = [parser parse:error];
+
+ return selector;
+}
+
+#pragma mark - Init
+
+- (instancetype)initWithString:(NSString *)string
+{
+ self = [super init];
+ if (self) {
+ _string = [self preprocessInput:string];
+ _location = 0;
+ }
+ return self;
+}
+
+- (NSString *)preprocessInput:(NSString *)string
+{
+ string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ string = [string stringByReplacingOccurrencesOfString:@"\r\n" withString:@"\n"];
+ string = [string stringByReplacingOccurrencesOfString:@"\r" withString:@"\n"];
+ string = [string stringByReplacingOccurrencesOfString:@"\f" withString:@"\n"];
+ string = [string stringByReplacingOccurrencesOfString:@"\0" withString:@"\uFFFD"];
+
+ return string;
+}
+
+#pragma mark - Errors
+
+- (void)emitError:(NSError * __autoreleasing *)error reason:(NSString *)reason location:(NSUInteger)location
+{
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: @"Error parsing selector",
+ NSLocalizedFailureReasonErrorKey: reason,
+ CSSSelectorStringKey: _string,
+ CSSSelectorErrorLocationKey: @(location)
+ };
+
+ if(error) {
+ *error = [NSError errorWithDomain:HTMLKitSelectorErrorDomain code:HTMLKitSelectorParseError userInfo:userInfo];
+ }
+}
+
+#pragma mark - Parsing
+
+- (CSSSelector *)parse:(NSError * __autoreleasing *)error
+{
+ if (_string.length == 0) {
+ [self emitError:error reason:@"Empty selector" location:0];
+ return nil;
+ }
+
+ NSArray *allSubSelectors = [_string componentsSeparatedByString:@","];
+ NSMutableArray *parsed = [NSMutableArray array];
+
+ for (NSString *subSelector in allSubSelectors) {
+ if ([subSelector isEqualToString:@""]) {
+ [self emitError:error reason:@"Empty selector" location:_location];
+ break;
+ }
+
+ CSSSelector *selector = [self parseSelector:subSelector error:error];
+ if (selector == nil) {
+ break;
+ }
+ [parsed addObject:selector];
+
+ _location += subSelector.length;
+ }
+
+ if (*error != nil) {
+ return nil;
+ }
+
+ if (parsed.count > 1) {
+ return anyOf(parsed);
+ }
+
+ return parsed.firstObject;
+}
+
+- (CSSSelector *)parseSelector:(NSString *)selectorString error:(NSError * __autoreleasing *)error
+{
+ _inputStream = [[CSSInputStream alloc] initWithString:selectorString];
+ [_inputStream consumeWhitespace];
+
+ CSSSelector *result = nil;
+
+ while (YES) {
+ CSSSelector *selector = [self parseSequenceOfSimpleSelectors:error];
+ if (selector == nil) {
+ break;
+ }
+
+ result = result ? allOf(@[result, selector]) : selector;
+
+ UTF32Char next = _inputStream.nextInputCharacter;
+
+ if (isCombinator(next)) {
+ NSString *combinator = [_inputStream consumeCombinator];
+
+ if ([combinator isEqualToString:@""]) {
+ result = descendantOfElementSelector(result);
+ } else if ([combinator isEqualToString:@">"]) {
+ result = childOfElementSelector(result);
+ } else if ([combinator isEqualToString:@"+"]) {
+ result = adjacentSiblingSelector(result);
+ } else if ([combinator isEqualToString:@"~"]) {
+ result = generalSiblingSelector(result);
+ }
+ }
+ }
+
+ return result;
+}
+
+- (CSSSelector *)parseSequenceOfSimpleSelectors:(NSError * __autoreleasing *)error
+{
+ NSMutableArray *selectors = [NSMutableArray array];
+
+ CSSSelector *typeSelector = [self parseTypeSelector:error];
+ if (typeSelector != nil) {
+ [selectors addObject:typeSelector];
+ }
+
+ while (YES) {
+ UTF32Char next = _inputStream.nextInputCharacter;
+ if (next == EOF || isCombinator(next)) {
+ break;
+ }
+
+ CSSSelector *simpleSelector = [self parseSimpleSelector:error];
+ if (simpleSelector == nil) {
+ return nil;
+ }
+ [selectors addObject:simpleSelector];
+ }
+
+ if (selectors.count > 1) {
+ return allOf(selectors);
+ }
+
+ return selectors.firstObject;
+}
+
+- (CSSSelector *)parseTypeSelector:(NSError * __autoreleasing *)error
+{
+ NSString *identifier = [_inputStream consumeIdentifier];
+ if (identifier != nil) {
+ return typeSelector(identifier);
+ }
+
+ if ([_inputStream consumeCharacter:ASTERIX]) {
+ return universalSelector();
+ }
+
+ return nil;
+}
+
+- (CSSSelector *)parseSimpleSelector:(NSError * __autoreleasing *)error
+{
+ CSSSelector *typeSelector = [self parseTypeSelector:error];
+ if (typeSelector != nil) {
+ return typeSelector;
+ }
+
+ UTF32Char codePoint = [_inputStream consumeNextInputCharacter];
+ switch (codePoint) {
+ case NUMBER_SIGN:
+ {
+ NSString *elementId = [_inputStream consumeIdentifier];
+ if (elementId == nil) {
+ [self emitError:error reason:@"Invalid character" location:_location + _inputStream.currentLocation];
+ return nil;
+ }
+ return idSelector(elementId);
+ }
+ case FULL_STOP:
+ {
+ NSString *className = [_inputStream consumeIdentifier];
+ if (className == nil) {
+ [self emitError:error reason:@"Invalid character" location:_location + _inputStream.currentLocation];
+ return nil;
+ }
+ return classSelector(className);
+ }
+ case LEFT_SQUARE_BRACKET:
+ {
+ return [self parseAttributeSelector:error];
+ }
+ case COLON:
+ {
+ return [self parsePseudoSelector:error];
+ }
+ default:
+ {
+ [self emitError:error reason:@"Invalid character" location:_location + _inputStream.currentLocation];
+ return nil;
+ }
+ }
+}
+
+- (CSSSelector *)parseAttributeSelector:(NSError * __autoreleasing *)error
+{
+ NSString *attribute = [_inputStream consumeIdentifier];
+ if (attribute == nil) {
+ [self emitError:error reason:@"Invalid character" location:_location + _inputStream.currentLocation + 1];
+ return nil;
+ }
+ [_inputStream consumeWhitespace];
+
+ CSSAttributeSelectorType type = CSSAttributeSelectorExists;
+
+ NSString *operator = [_inputStream consumeCharactersInString:@"=~|^$*!"];
+
+ if ([operator isEqualToString:@"="]) {
+ type = CSSAttributeSelectorExactMatch;
+ } else if ([operator isEqualToString:@"~="]) {
+ type = CSSAttributeSelectorIncludes;
+ } else if ([operator isEqualToString:@"|="]) {
+ type = CSSAttributeSelectorHyphen;
+ } else if ([operator isEqualToString:@"^="]) {
+ type = CSSAttributeSelectorBegins;
+ } else if ([operator isEqualToString:@"$="]) {
+ type = CSSAttributeSelectorEnds;
+ } else if ([operator isEqualToString:@"*="]) {
+ type = CSSAttributeSelectorContains;
+ } else if ([operator isEqualToString:@"!="]) {
+ type = CSSAttributeSelectorNot;
+ }
+
+ NSString *value = nil;
+ [_inputStream consumeWhitespace];
+
+ UTF32Char next = _inputStream.nextInputCharacter;
+ if (isQuote(next)) {
+ UTF32Char quote = [_inputStream consumeNextInputCharacter];
+ value = [_inputStream consumeStringWithEndingCodePoint:quote];
+ } else {
+ value = [_inputStream consumeIdentifier];
+ }
+
+ [_inputStream consumeWhitespace];
+
+ // Consume RIGHT_SQUARE_BRACKET
+ if (![_inputStream consumeCharacter:RIGHT_SQUARE_BRACKET]) {
+ [self emitError:error reason:@"Expected closing right square bracket ']'" location:_location + _inputStream.currentLocation];
+ }
+
+ if (type == CSSAttributeSelectorExists) {
+ return hasAttributeSelector(attribute);
+ }
+
+ return attributeSelector(type, attribute, value);
+}
+
+- (CSSSelector *)parsePseudoSelector:(NSError * __autoreleasing *)error
+{
+ NSString *pseudoClass = [_inputStream consumeIdentifier];
+
+ if ([pseudoClass hasPrefix:@"nth"]) {
+ [_inputStream consumeWhitespace];
+ if (![_inputStream consumeCharacter:LEFT_PARENTHESIS]) {
+ [self emitError:error reason:@"Expected opening left parenthesis '('" location:_location + _inputStream.currentLocation];
+ }
+
+ NSString *functionExpression = [_inputStream consumeCharactersUpToString:@")"];
+ CSSNthExpression expression = [CSSNthExpressionParser parseExpression:functionExpression];
+
+ [_inputStream consumeWhitespace];
+ if (![_inputStream consumeCharacter:RIGHT_PARENTHESIS]) {
+ [self emitError:error reason:@"Expected closing right parenthesis ')'" location:_location + _inputStream.currentLocation];
+ }
+
+ if ([pseudoClass isEqualToString:@"nth-child"]) {
+ return nthChildSelector(expression);
+ } else if ([pseudoClass isEqualToString:@"nth-last-child"]) {
+ return nthLastChildSelector(expression);
+ } else if ([pseudoClass isEqualToString:@"nth-of-type"]) {
+ return nthOfTypeSelector(expression);
+ } else if ([pseudoClass isEqualToString:@"nth-last-of-type"]) {
+ return nthLastOfTypeSelector(expression);
+ }
+ } else if ([pseudoClass isEqualToString:@"not"]) {
+ [_inputStream consumeWhitespace];
+ if (![_inputStream consumeCharacter:LEFT_PARENTHESIS]) {
+ [self emitError:error reason:@"Expected opening left parenthesis '('" location:_location + _inputStream.currentLocation];
+ }
+
+ CSSSelector *subSelector = [self parseSimpleSelector:error];
+ [_inputStream consumeWhitespace];
+ if (![_inputStream consumeCharacter:RIGHT_PARENTHESIS]) {
+ [self emitError:error reason:@"Expected closing right parenthesis ')'" location:_location + _inputStream.currentLocation];
+ }
+
+ return nay(subSelector);
+ } else {
+ if ([pseudoClass isEqualToString:@"even"]) {
+ return evenSlector();
+ } else if ([pseudoClass isEqualToString:@"odd"]) {
+ return oddSelector();
+ } else if ([pseudoClass isEqualToString:@"first-child"]) {
+ return firstChildSelector();
+ } else if ([pseudoClass isEqualToString:@"last-child"]) {
+ return lastChildSelector();
+ } else if ([pseudoClass isEqualToString:@"first-of-type"]) {
+ return firstOfTypeSelector();
+ } else if ([pseudoClass isEqualToString:@"last-of-type"]) {
+ return lastOfTypeSelector();
+ } else if ([pseudoClass isEqualToString:@"only-child"]) {
+ return onlyChildSelector();
+ } else if ([pseudoClass isEqualToString:@"only-of-type"]) {
+ return onlyOfTypeSelector();
+ } else if ([pseudoClass isEqualToString:@"root"]) {
+ return rootSelector();
+ } else if ([pseudoClass isEqualToString:@"empty"]) {
+ return emptySelector();
+ } else if ([pseudoClass isEqualToString:@"link"]) {
+ return linkSelector();
+ } else if ([pseudoClass isEqualToString:@"enabled"]) {
+ return enabledSelector();
+ } else if ([pseudoClass isEqualToString:@"disabled"]) {
+ return disabledSelector();
+ } else if ([pseudoClass isEqualToString:@"checked"]) {
+ return checkedSelector();
+ }
+ }
+ NSString *reason = [NSString stringWithFormat:@"Unknown pseudo class: %@", pseudoClass];
+ [self emitError:error reason:reason location:_location + _inputStream.currentLocation];
+ return nil;
+}
+
+@end
diff --git a/HTMLKit/CSSSelectors.h b/HTMLKit/CSSSelectors.h
new file mode 100644
index 0000000..bf9c1af
--- /dev/null
+++ b/HTMLKit/CSSSelectors.h
@@ -0,0 +1,71 @@
+//
+// CSSSelectors.h
+// HTMLKit
+//
+// Created by Iska on 14/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelector.h"
+#import "CSSStructuralPseudoSelectors.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - Type Selectors
+
+extern CSSSelector * universalSelector();
+extern CSSSelector * typeSelector(NSString *type);
+
+#pragma mark - Atribute Selectors
+
+extern CSSSelector * idSelector(NSString *elementId);
+extern CSSSelector * classSelector(NSString *className);
+extern CSSSelector * hasAttributeSelector(NSString *attribute);
+extern CSSSelector * attributeSelector(CSSAttributeSelectorType type,
+ NSString *attribute,
+ NSString *value);
+
+#pragma mark - Nth-Expression Selectors
+
+extern CSSSelector * nthChildSelector(CSSNthExpression expression);
+extern CSSSelector * nthLastChildSelector(CSSNthExpression expression);
+extern CSSSelector * nthOfTypeSelector(CSSNthExpression expression);
+extern CSSSelector * nthLastOfTypeSelector(CSSNthExpression expression);
+
+extern CSSSelector * oddSelector();
+extern CSSSelector * evenSlector();
+
+extern CSSSelector * firstChildSelector();
+extern CSSSelector * lastChildSelector();
+extern CSSSelector * firstOfTypeSelector();
+extern CSSSelector * lastOfTypeSelector();
+
+extern CSSSelector * onlyChildSelector();
+extern CSSSelector * onlyOfTypeSelector();
+
+#pragma mark - Combinators
+
+extern CSSSelector * childOfElementSelector(CSSSelector *selector);
+extern CSSSelector * descendantOfElementSelector(CSSSelector *selector);
+extern CSSSelector * adjacentSiblingSelector(CSSSelector *selector);
+extern CSSSelector * generalSiblingSelector(CSSSelector *selector);
+
+#pragma mark - Pseudo Functions
+
+extern CSSSelector * nay(CSSSelector *selector);
+extern CSSSelector * has(CSSSelector *selector);
+
+#pragma mark - Compound Selectors
+
+extern CSSSelector * allOf(NSArray *selectors);
+extern CSSSelector * anyOf(NSArray *selectors);
+
+#pragma mark - Pseudo
+
+extern CSSSelector * namedPseudoSelector(NSString *name, CSSSelector *selector);
+
+#pragma mark - Block
+
+extern CSSSelector * namedBlockSelector(NSString *name, BOOL (^ acceptBlock)(HTMLElement *element));
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSSelectors.m b/HTMLKit/CSSSelectors.m
new file mode 100644
index 0000000..d4e864f
--- /dev/null
+++ b/HTMLKit/CSSSelectors.m
@@ -0,0 +1,177 @@
+//
+// CSSSelectors.m
+// HTMLKit
+//
+// Created by Iska on 19/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelectors.h"
+#import "CSSTypeSelector.h"
+#import "CSSAttributeSelector.h"
+#import "CSSPseudoClassSelector.h"
+#import "CSSPseudoFunctionSelector.h"
+#import "CSSNthExpressionSelector.h"
+#import "CSSCombinatorSelector.h"
+#import "CSSCompoundSelector.h"
+#import "CSSSelectorBlock.h"
+
+#pragma mark - Type Selectors
+
+CSSSelector * universalSelector()
+{
+ return [CSSTypeSelector universalSelector];
+}
+
+CSSSelector * typeSelector(NSString *type)
+{
+ return [[CSSTypeSelector alloc] initWithType:type];
+}
+
+#pragma mark - Atribute Selectors
+
+CSSSelector * idSelector(NSString *elementId)
+{
+ return [CSSAttributeSelector idSelector:elementId];
+}
+
+CSSSelector * classSelector(NSString *className)
+{
+ return [CSSAttributeSelector classSelector:className];
+}
+
+CSSSelector * hasAttributeSelector(NSString *attribute)
+{
+ return [CSSAttributeSelector hasAttributeSelector:attribute];
+}
+
+CSSSelector * attributeSelector(CSSAttributeSelectorType type,
+ NSString *attribute,
+ NSString *value)
+{
+ return [[CSSAttributeSelector alloc] initWithType:type attributeName:attribute attrbiuteValue:value];
+}
+
+#pragma mark - Nth-Expression Selectors
+
+CSSSelector * nthChildSelector(CSSNthExpression expression)
+{
+ return [CSSNthExpressionSelector nthChildSelector:expression];
+}
+
+CSSSelector * nthLastChildSelector(CSSNthExpression expression)
+{
+ return [CSSNthExpressionSelector nthLastChildSelector:expression];
+}
+
+CSSSelector * nthOfTypeSelector(CSSNthExpression expression)
+{
+ return [CSSNthExpressionSelector nthOfTypeSelector:expression];
+}
+
+CSSSelector * nthLastOfTypeSelector(CSSNthExpression expression)
+{
+ return [CSSNthExpressionSelector nthLastOfTypeSelector:expression];
+}
+
+#pragma mark - Nth-Expression Shorthand
+
+CSSSelector * oddSelector()
+{
+ return namedPseudoSelector(@"odd", nthChildSelector(CSSNthExpressionOdd));
+}
+
+CSSSelector * evenSlector()
+{
+ return namedPseudoSelector(@"even", nthChildSelector(CSSNthExpressionEven));
+}
+
+CSSSelector * firstChildSelector()
+{
+ return namedPseudoSelector(@"first-child", nthChildSelector(CSSNthExpressionMake(0, 1)));
+}
+
+CSSSelector * lastChildSelector()
+{
+ return namedPseudoSelector(@"last-child", nthLastChildSelector(CSSNthExpressionMake(0, 1)));
+}
+
+CSSSelector * firstOfTypeSelector()
+{
+ return namedPseudoSelector(@"first-of-type", nthOfTypeSelector(CSSNthExpressionMake(0, 1)));
+}
+
+CSSSelector * lastOfTypeSelector()
+{
+ return namedPseudoSelector(@"last-of-type", nthLastOfTypeSelector(CSSNthExpressionMake(0, 1)));
+}
+
+CSSSelector * onlyChildSelector()
+{
+ return namedPseudoSelector(@"only-child", allOf(@[firstChildSelector(), lastChildSelector()]));
+}
+
+CSSSelector * onlyOfTypeSelector()
+{
+ return namedPseudoSelector(@"only-of-type", allOf(@[firstOfTypeSelector(), lastOfTypeSelector()]));
+}
+
+#pragma mark - Combinators
+
+CSSSelector * childOfElementSelector(CSSSelector *selector)
+{
+ return [CSSCombinatorSelector childOfElementCombinator:selector];
+}
+
+CSSSelector * descendantOfElementSelector(CSSSelector *selector)
+{
+ return [CSSCombinatorSelector descendantOfElementCombinator:selector];
+}
+
+CSSSelector * adjacentSiblingSelector(CSSSelector *selector)
+{
+ return [CSSCombinatorSelector adjacentSiblingCombinator:selector];
+}
+
+CSSSelector * generalSiblingSelector(CSSSelector *selector)
+{
+ return [CSSCombinatorSelector generalSiblingCombinator:selector];
+}
+
+#pragma mark - Pseudo Functions
+
+CSSSelector * nay(CSSSelector *selector)
+{
+ return [CSSPseudoFunctionSelector notSelector:selector];
+}
+
+CSSSelector * has(CSSSelector *selector)
+{
+ return [CSSPseudoFunctionSelector hasSelector:selector];
+}
+
+#pragma mark - Compound Selectors
+
+CSSSelector * allOf( NSArray * selectors)
+{
+ return [CSSCompoundSelector andSelector:selectors];
+}
+
+CSSSelector * anyOf( NSArray * selectors)
+{
+ return [CSSCompoundSelector orSelector:selectors];
+}
+
+#pragma mark - Pseudo
+
+CSSSelector * namedPseudoSelector(NSString *name, CSSSelector *selector)
+{
+ return [[CSSPseudoClassSelector alloc] initWithClassName:name selector:selector];
+}
+
+#pragma mark - Block
+
+CSSSelector * namedBlockSelector(NSString *name, BOOL (^ acceptBlock)(HTMLElement *element))
+{
+ return [[CSSSelectorBlock alloc] initWithName:name block:acceptBlock];
+}
diff --git a/HTMLKit/CSSStructuralPseudoSelectors.h b/HTMLKit/CSSStructuralPseudoSelectors.h
new file mode 100644
index 0000000..d004235
--- /dev/null
+++ b/HTMLKit/CSSStructuralPseudoSelectors.h
@@ -0,0 +1,40 @@
+//
+// CSSStructuralPseudoSelector.h
+// HTMLKit
+//
+// Created by Iska on 11/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+@class CSSSelector;
+
+NS_ASSUME_NONNULL_BEGIN
+
+extern CSSSelector * rootSelector();
+extern CSSSelector * emptySelector();
+extern CSSSelector * parentSelector();
+
+extern CSSSelector * buttonSelector();
+extern CSSSelector * checkboxSelector();
+extern CSSSelector * fileSelector();
+extern CSSSelector * headerSelector();
+extern CSSSelector * imageSelector();
+extern CSSSelector * inputSelector();
+extern CSSSelector * linkSelector();
+extern CSSSelector * passwordSelector();
+extern CSSSelector * radioSelector();
+extern CSSSelector * resetSelector();
+extern CSSSelector * submitSelector();
+extern CSSSelector * textSelector();
+
+extern CSSSelector * enabledSelector();
+extern CSSSelector * disabledSelector();
+extern CSSSelector * checkedSelector();
+extern CSSSelector * optionalSelector();
+extern CSSSelector * requiredSelector();
+
+extern CSSSelector * ltSelector();
+extern CSSSelector * gtSelector();
+extern CSSSelector * eqSelector();
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSStructuralPseudoSelectors.m b/HTMLKit/CSSStructuralPseudoSelectors.m
new file mode 100644
index 0000000..0b4bd43
--- /dev/null
+++ b/HTMLKit/CSSStructuralPseudoSelectors.m
@@ -0,0 +1,301 @@
+//
+// CSSStructuralPseudoSelector.m
+// HTMLKit
+//
+// Created by Iska on 11/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSStructuralPseudoSelectors.h"
+#import "CSSSelectors.h"
+#import "HTMLElement.h"
+#import "NSString+HTMLKit.h"
+
+#pragma mark - Elements
+
+CSSSelector * rootSelector()
+{
+ return namedBlockSelector(@":root", ^BOOL(HTMLElement * element) {
+ return element.parentElement == nil;
+ });
+}
+
+CSSSelector * emptySelector()
+{
+ return namedBlockSelector(@":empty", ^BOOL(HTMLElement * element) {
+ for (HTMLNode *child in element.childNodes) {
+ if (child.nodeType == HTMLNodeElement) {
+ return NO;
+ } else if (child.nodeType == HTMLNodeText && child.textContent.length > 0) {
+ return NO;
+ }
+ }
+ return YES;
+ });
+}
+
+CSSSelector * parentSelector()
+{
+ return namedBlockSelector(@":parent", ^BOOL(HTMLElement * element) {
+ return element.childNodesCount > 0;
+ });
+}
+
+CSSSelector * buttonSelector()
+{
+ return namedBlockSelector(@":button", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element.tagName isEqualToString:@"button"]) {
+ return YES;
+ }
+ if ([element.tagName isEqualToString:@"input"] && [element[@"type"] isEqualToString:@"button"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * checkboxSelector()
+{
+ return namedBlockSelector(@":checkbox", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element[@"type"] isEqualToString:@"checkbox"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * fileSelector()
+{
+ return namedBlockSelector(@":file", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element[@"type"] isEqualToString:@"file"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * headerSelector()
+{
+ return namedBlockSelector(@":header", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element.tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * imageSelector()
+{
+ return namedBlockSelector(@":image", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element[@"type"] isEqualToString:@"image"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * inputSelector()
+{
+ return namedBlockSelector(@":input", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element.tagName isEqualToAny:@"button", @"input", @"select", @"textarea", nil]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * linkSelector()
+{
+ // https://html.spec.whatwg.org/multipage/scripting.html#selector-link
+ return namedBlockSelector(@":link", ^BOOL(HTMLElement * element) {
+ if ([element hasAttribute:@"href"]) {
+ return [element.tagName isEqualToAny:@"a", @"area", @"link", nil];
+ }
+ return NO;
+ });
+}
+
+CSSSelector * passwordSelector()
+{
+ return namedBlockSelector(@":password", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element[@"type"] isEqualToString:@"password"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * radioSelector()
+{
+ return namedBlockSelector(@":radio", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element[@"type"] isEqualToString:@"radio"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * resetSelector()
+{
+ return namedBlockSelector(@":reset", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element[@"type"] isEqualToString:@"reset"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * submitSelector()
+{
+ return namedBlockSelector(@":submit", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element.tagName isEqualToString:@"input"] && [element[@"type"] isEqualToString:@"submit"]) {
+ return YES;
+ }
+ if ([element.tagName isEqualToString:@"button"] && [element[@"type"] isEqualToString:@"submit"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+CSSSelector * textSelector()
+{
+ return namedBlockSelector(@":text", ^BOOL(HTMLElement * _Nonnull element) {
+ if ([element[@"type"] isEqualToString:@"text"]) {
+ return YES;
+ }
+ return NO;
+ });
+}
+
+#pragma mark - State
+
+CSSSelector * enabledSelector()
+{
+ // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
+ CSSSelector *candiate = anyOf(@[
+ typeSelector(@"button"),
+ typeSelector(@"input"),
+ typeSelector(@"select"),
+ typeSelector(@"textarea"),
+ typeSelector(@"optgroup"),
+ typeSelector(@"option"),
+ typeSelector(@"menuitem"),
+ typeSelector(@"fieldset"),
+ ]);
+ return namedPseudoSelector(@"enabled", allOf(@[candiate, nay(disabledSelector())]));
+}
+
+CSSSelector * disabledSelector()
+{
+ // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
+ CSSSelector *disabledAttribute = hasAttributeSelector(@"disabled");
+
+ // https://html.spec.whatwg.org/multipage/forms.html#concept-fieldset-disabled
+ CSSSelector *disabledFieldset = allOf(@[typeSelector(@"fieldset"), disabledAttribute]);
+ CSSSelector *firstLegend = allOf(@[typeSelector(@"legend"), firstOfTypeSelector()]);
+ CSSSelector *firstLegendDecendantDisabledFieldSet = allOf(@[firstLegend, descendantOfElementSelector(disabledFieldset)]);
+
+ // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
+ CSSSelector *disabledForm = anyOf(@[
+ anyOf(@[
+ allOf(@[typeSelector(@"button"), disabledAttribute]),
+ allOf(@[typeSelector(@"input"), disabledAttribute]),
+ allOf(@[typeSelector(@"select"), disabledAttribute]),
+ allOf(@[typeSelector(@"textarea"), disabledAttribute])
+ ]),
+ allOf(@[
+ descendantOfElementSelector(disabledFieldset),
+ nay(firstLegendDecendantDisabledFieldSet)
+ ])
+ ]);
+
+ // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
+ CSSSelector *disabledMenuItem = allOf(@[typeSelector(@"menuitem"), disabledAttribute]);
+ CSSSelector *disabledOptgroup = allOf(@[typeSelector(@"optgroup"), disabledAttribute]);
+
+ // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
+ CSSSelector *disabledOption = allOf(@[
+ typeSelector(@"option"),
+ anyOf(@[
+ disabledAttribute,
+ descendantOfElementSelector(disabledOptgroup)])
+ ]);
+ return namedPseudoSelector(@"disabled",
+ anyOf(@[disabledOption, disabledOptgroup, disabledMenuItem, disabledForm, disabledFieldset]));
+}
+
+CSSSelector * checkedSelector()
+{
+ // https://html.spec.whatwg.org/multipage/scripting.html#selector-checked
+ CSSSelector *candidate = anyOf(@[
+ typeSelector(@"input"),
+ typeSelector(@"option"),
+ typeSelector(@"menutitem")
+ ]);
+ CSSSelector *hasAttribute = anyOf(@[
+ hasAttributeSelector(@"checked"),
+ hasAttributeSelector(@"selected")
+ ]);
+
+ return namedPseudoSelector(@"checked", allOf(@[candidate, hasAttribute]));
+}
+
+CSSSelector * optionalSelector()
+{
+ // https://html.spec.whatwg.org/multipage/scripting.html#selector-optional
+ CSSSelector *candidate = anyOf(@[
+ typeSelector(@"input"),
+ typeSelector(@"select"),
+ typeSelector(@"textarea")
+ ]);
+ CSSSelector *noAttribute = nay(hasAttributeSelector(@"required"));
+
+ return namedPseudoSelector(@"optional", allOf(@[candidate, noAttribute]));
+}
+
+CSSSelector * requiredSelector()
+{
+ // https://html.spec.whatwg.org/multipage/scripting.html#selector-required
+ // https://html.spec.whatwg.org/multipage/forms.html#concept-input-required
+ CSSSelector *candidate = anyOf(@[
+ typeSelector(@"input"),
+ typeSelector(@"select"),
+ typeSelector(@"textarea")
+ ]);
+ CSSSelector *hasAttribute = hasAttributeSelector(@"required");
+
+ return namedPseudoSelector(@"required", allOf(@[candidate, hasAttribute]));
+}
+
+#pragma mark - Positional
+
+CSSSelector * ltSelector(NSUInteger index)
+{
+ NSString *name = [NSString stringWithFormat:@":lt(%lu)", (unsigned long)index];
+ return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
+ return [element.parentElement indexOfChildNode:element] < index;
+ });
+}
+
+CSSSelector * gtSelector(NSUInteger index)
+{
+ NSString *name = [NSString stringWithFormat:@":gt(%lu)", (unsigned long)index];
+ return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
+ return [element.parentElement indexOfChildNode:element] > index;
+ });
+}
+
+CSSSelector * eqSelector(NSInteger index)
+{
+ NSString *name = [NSString stringWithFormat:@":eq(%lu)", (unsigned long)index];
+ return namedBlockSelector(name, ^BOOL(HTMLElement * _Nonnull element) {
+ NSUInteger elementIndex = [element.parentElement indexOfChildNode:element];
+
+ if (index > 0) {
+ return elementIndex == index;
+ } else {
+ return elementIndex == element.parentElement.childNodesCount - index - 1;
+ }
+ });
+}
diff --git a/HTMLKit/CSSTypeSelector.h b/HTMLKit/CSSTypeSelector.h
new file mode 100644
index 0000000..6bd1f93
--- /dev/null
+++ b/HTMLKit/CSSTypeSelector.h
@@ -0,0 +1,24 @@
+//
+// CSSTypeSelector.h
+// HTMLKit
+//
+// Created by Iska on 13/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSSelector.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface CSSTypeSelector : CSSSelector
+
+@property (nonatomic, strong, readonly) NSString *type;
+
++ (instancetype)universalSelector;
+
+- (instancetype)initWithType:(NSString *)type;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/HTMLKit/CSSTypeSelector.m b/HTMLKit/CSSTypeSelector.m
new file mode 100644
index 0000000..053ec74
--- /dev/null
+++ b/HTMLKit/CSSTypeSelector.m
@@ -0,0 +1,50 @@
+//
+// CSSTypeSelector.m
+// HTMLKit
+//
+// Created by Iska on 13/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSTypeSelector.h"
+#import "HTMLElement.h"
+#import "NSString+HTMLKit.h"
+
+@interface CSSTypeSelector ()
+{
+ NSString *_type;
+}
+@end
+
+@implementation CSSTypeSelector
+
++ (instancetype)universalSelector
+{
+ return [[self alloc] initWithType:@"*"];
+}
+
+- (instancetype)initWithType:(NSString *)type
+{
+ self = [super init];
+ if (self) {
+ _type = [type copy];
+ }
+ return self;
+}
+
+- (BOOL)acceptElement:(HTMLElement *)element
+{
+ if ([_type isEqualToString:@"*"] || [_type isEqualToStringIgnoringCase:element.tagName]) {
+ return YES;
+ }
+ return NO;
+}
+
+#pragma mark - Description
+
+- (NSString *)debugDescription
+{
+ return self.type;
+}
+
+@end
diff --git a/HTMLKit/HTMLDocumentFragment.m b/HTMLKit/HTMLDocumentFragment.m
index adcde5e..2f2d5a2 100644
--- a/HTMLKit/HTMLDocumentFragment.m
+++ b/HTMLKit/HTMLDocumentFragment.m
@@ -32,7 +32,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/HTMLElement.m b/HTMLKit/HTMLElement.m
index 6083537..9ebc3ad 100644
--- a/HTMLKit/HTMLElement.m
+++ b/HTMLKit/HTMLElement.m
@@ -180,4 +180,9 @@
return description;
}
+- (NSString *)debugDescription
+{
+ return self.description;
+}
+
@end
diff --git a/HTMLKit/HTMLInputStreamReader.h b/HTMLKit/HTMLInputStreamReader.h
index 1ffdce8..dea5e29 100644
--- a/HTMLKit/HTMLInputStreamReader.h
+++ b/HTMLKit/HTMLInputStreamReader.h
@@ -24,6 +24,7 @@ typedef void (^ HTMLStreamReaderErrorCallback)(NSString *reason);
- (UTF32Char)currentInputCharacter;
- (UTF32Char)nextInputCharacter;
+- (UTF32Char)inputCharacterPointAtOffset:(NSUInteger)offset;
- (UTF32Char)consumeNextInputCharacter;
- (void)reconsumeCurrentInputCharacter;
@@ -35,6 +36,7 @@ typedef void (^ HTMLStreamReaderErrorCallback)(NSString *reason);
- (BOOL)consumeString:(NSString *)string caseSensitive:(BOOL)caseSensitive;
- (NSString *)consumeCharactersUpToCharactersInString:(NSString *)characters;
- (NSString *)consumeCharactersUpToString:(NSString *)string;
+- (NSString *)consumeCharactersInString:(NSString *)characters;
- (NSString *)consumeAlphanumericCharacters;
- (void)markCurrentLocation;
diff --git a/HTMLKit/HTMLInputStreamReader.m b/HTMLKit/HTMLInputStreamReader.m
index b2fcb88..3df5f8d 100644
--- a/HTMLKit/HTMLInputStreamReader.m
+++ b/HTMLKit/HTMLInputStreamReader.m
@@ -8,6 +8,7 @@
#import "HTMLInputStreamReader.h"
#import "HTMLTokenizerCharacters.h"
+#import "NSCharacterSet+HTMLKit.h"
#pragma mark - HTMLInputStreamReader
@@ -39,6 +40,7 @@
if (self) {
_string = [string copy];
_scanner = [[NSScanner alloc] initWithString:string];
+ _scanner.charactersToBeSkipped = nil;
CFStringInitInlineBuffer((CFStringRef)_string, &_buffer, CFRangeMake(0, _string.length));
}
return self;
@@ -63,7 +65,6 @@
- (UTF32Char)nextInputCharacter
{
if (_reconsume) {
- _reconsume = NO;
return _currentInputCharacter;
}
@@ -106,6 +107,11 @@
return nextInputCharacter;
}
+- (UTF32Char)inputCharacterPointAtOffset:(NSUInteger)offset
+{
+ return CFStringGetCharacterFromInlineBuffer(&_buffer, _location + offset);
+}
+
- (UTF32Char)consumeNextInputCharacter
{
if (_reconsume) {
@@ -124,9 +130,12 @@
{
UTF32Char nextInputCharacter = [self nextInputCharacter];
if (nextInputCharacter == character) {
- _location += _consume;
- _scanner.scanLocation = _location;
- _currentInputCharacter = nextInputCharacter;
+ if (!_reconsume) {
+ _location += _consume;
+ _scanner.scanLocation = _location;
+ _currentInputCharacter = nextInputCharacter;
+ }
+ _reconsume = NO;
return YES;
}
return NO;
@@ -145,7 +154,7 @@
- (BOOL)consumeHexNumber:(unsigned long long *)result
{
- NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"];
+ NSCharacterSet *set = [NSCharacterSet HTMLHexNumberCharacterSet];
NSString *string = nil;
BOOL success = [_scanner scanCharactersFromSet:set intoString:&string];
@@ -193,6 +202,26 @@
return consumed;
}
+- (NSString *)consumeCharactersInString:(NSString *)characters
+{
+ NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:characters];
+
+ if (_reconsume) {
+ _scanner.scanLocation--;
+ }
+
+ NSString *string = nil;
+ BOOL success = [_scanner scanCharactersFromSet:set intoString:&string];
+ if (success == NO) {
+ _scanner.scanLocation++;
+ return nil;
+ }
+
+ _reconsume = NO;
+ _location = _scanner.scanLocation;
+ return string;
+}
+
- (NSString *)consumeAlphanumericCharacters
{
NSCharacterSet *set = [NSCharacterSet alphanumericCharacterSet];
diff --git a/HTMLKit/HTMLKit.h b/HTMLKit/HTMLKit.h
index 3b5c810..5d85025 100644
--- a/HTMLKit/HTMLKit.h
+++ b/HTMLKit/HTMLKit.h
@@ -6,8 +6,7 @@
// Copyright (c) 2014 BrainCookie. All rights reserved.
//
-#import
-
-@interface HTMLKit : NSObject
-
-@end
+#import "HTMLParser.h"
+#import "HTMLDOM.h"
+#import "CSSSelectors.h"
+#import "HTMLKitErrorDomain.h"
diff --git a/HTMLKit/HTMLKit.m b/HTMLKit/HTMLKit.m
deleted file mode 100644
index da149aa..0000000
--- a/HTMLKit/HTMLKit.m
+++ /dev/null
@@ -1,13 +0,0 @@
-//
-// HTMLKit.m
-// HTMLKit
-//
-// Created by Iska on 15/09/14.
-// Copyright (c) 2014 BrainCookie. All rights reserved.
-//
-
-#import "HTMLKit.h"
-
-@implementation HTMLKit
-
-@end
diff --git a/HTMLKit/HTMLKitErrorDomain.h b/HTMLKit/HTMLKitErrorDomain.h
new file mode 100644
index 0000000..98ca6f8
--- /dev/null
+++ b/HTMLKit/HTMLKitErrorDomain.h
@@ -0,0 +1,23 @@
+//
+// HTMLKitErrorDomain.h
+// HTMLKit
+//
+// Created by Iska on 24/11/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#ifndef HTMLKitErrorDomain_h
+#define HTMLKitErrorDomain_h
+
+static NSString *const HTMLKitErrorDomain = @"HTMLKit";
+static NSString *const HTMLKitSelectorErrorDomain = @"HTMLKitSelector";
+
+static NSString *const CSSSelectorStringKey = @"CSSSelectorString";
+static NSString *const CSSSelectorErrorLocationKey = @"CSSSelectorErrorLocation";
+
+NS_ENUM(NSInteger)
+{
+ HTMLKitSelectorParseError = 4200
+};
+
+#endif /* HTMLKitErrorDomain_h */
diff --git a/HTMLKit/HTMLNode.h b/HTMLKit/HTMLNode.h
index eb07dbc..e5fac40 100644
--- a/HTMLKit/HTMLNode.h
+++ b/HTMLKit/HTMLNode.h
@@ -38,6 +38,7 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
@class HTMLDocument;
@class HTMLElement;
+@class CSSSelector;
@interface HTMLNode : NSObject
@@ -61,6 +62,10 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
@property (nonatomic, strong, readonly) HTMLNode *nextSibling;
+@property (nonatomic, strong, readonly) HTMLElement *previousSiblingElement;
+
+@property (nonatomic, strong, readonly) HTMLElement *nextSiblingElement;
+
@property (nonatomic, copy) NSString *textContent;
@property (nonatomic, strong, readonly) NSString *outerHTML;
@@ -83,6 +88,12 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
- (NSUInteger)indexOfChildNode:(HTMLNode *)node;
+- (NSUInteger)childElementsCount;
+
+- (HTMLElement *)childElementAtIndex:(NSUInteger)index;
+
+- (NSUInteger)indexOfChildElement:(HTMLElement *)element;
+
- (HTMLNode *)prependNode:(HTMLNode *)node;
- (void)prependNodes:(NSArray *)nodes;
@@ -121,6 +132,9 @@ typedef NS_ENUM(unsigned short, HTMLDocumentPosition)
- (HTMLNodeIterator *)nodeIteratorWithShowOptions:(HTMLNodeFilterShowOptions)showOptions
filterBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))filter;
+- (HTMLElement *)firstElementMatchingSelector:(CSSSelector *)selector;
+- (NSArray *)elementsMatchingSelector:(CSSSelector *)selector;
+
- (NSString *)treeDescription;
@end
diff --git a/HTMLKit/HTMLNode.m b/HTMLKit/HTMLNode.m
index fdff072..c630665 100644
--- a/HTMLKit/HTMLNode.m
+++ b/HTMLKit/HTMLNode.m
@@ -13,6 +13,7 @@
#import "HTMLText.h"
#import "HTMLComment.h"
#import "HTMLKitDOMExceptions.h"
+#import "CSSSelector.h"
@interface HTMLDocument (Private)
- (void)runRemovingStepsForNode:(HTMLNode *)oldNode
@@ -98,6 +99,24 @@
return [_parentNode childNodeAtIndex:index + 1];
}
+- (HTMLElement *)previousSiblingElement
+{
+ HTMLNode *node = self.previousSibling;
+ while (node && node.nodeType != HTMLNodeElement) {
+ node = node.previousSibling;
+ }
+ return node.asElement;
+}
+
+- (HTMLElement *)nextSiblingElement
+{
+ HTMLNode *node = self.previousSibling;
+ while (node && node.nodeType != HTMLNodeElement) {
+ node = node.nextSibling;
+ }
+ return node.asElement;
+}
+
- (NSString *)textContent
{
return nil;
@@ -140,11 +159,46 @@
return [self.childNodes objectAtIndex:index];
}
+- (NSUInteger)childElementsCount
+{
+ return [self.childNodes indexesOfObjectsPassingTest:^BOOL(HTMLNode * _Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
+ return node.nodeType == HTMLNodeElement;
+ }].count;
+}
+
- (NSUInteger)indexOfChildNode:(HTMLNode *)node
{
return [self.childNodes indexOfObject:node];
}
+- (HTMLElement *)childElementAtIndex:(NSUInteger)index
+{
+ NSUInteger counter = 0;
+ for (HTMLNode *node in self.childNodes) {
+ if (node.nodeType == HTMLNodeElement) {
+ if (counter == index) {
+ return node.asElement;
+ }
+ counter++;
+ }
+ }
+ return nil;
+}
+
+- (NSUInteger)indexOfChildElement:(HTMLElement *)element
+{
+ NSUInteger counter = 0;
+ for (HTMLNode *node in self.childNodes) {
+ if (node.nodeType == HTMLNodeElement) {
+ if (node == element) {
+ return counter;
+ }
+ counter++;
+ }
+ }
+ return NSNotFound;
+}
+
- (HTMLNode *)prependNode:(HTMLNode *)node
{
return [self insertNode:node beforeChildNode:self.firstChild];
@@ -386,6 +440,29 @@
return [HTMLNodeIterator iteratorWithNode:self showOptions:showOptions filter:filter];
}
+#pragma mark - Selectors
+
+- (HTMLElement *)firstElementMatchingSelector:(CSSSelector *)selector
+{
+ for (HTMLElement *element in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
+ if ([selector acceptElement:element]) {
+ return element;
+ }
+ }
+ return nil;
+}
+
+- (NSArray *)elementsMatchingSelector:(CSSSelector *)selector
+{
+ NSMutableArray *result = [NSMutableArray array];
+ for (HTMLElement *element in [self nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:nil]) {
+ if ([selector acceptElement:element]) {
+ [result addObject:element];
+ }
+ }
+ return result;
+}
+
#ifndef HTMLKIT_NO_DOM_CHECKS
#pragma mark - Validity Checks
@@ -610,12 +687,7 @@ NS_INLINE void CheckInvalidCombination(HTMLNode *parent, HTMLNode *node, NSStrin
- (NSString *)description
{
- return [NSString stringWithFormat:@"<%@: %p %@>", self.class, self, self.name];
-}
-
-- (NSString *)debugDescription
-{
- return self.treeDescription;
+ return [NSString stringWithFormat:@"<%@: %p '%@'>", self.class, self, self.name];
}
- (id)debugQuickLookObject
diff --git a/HTMLKit/HTMLNodeFilter.h b/HTMLKit/HTMLNodeFilter.h
index a32a2ea..3046762 100644
--- a/HTMLKit/HTMLNodeFilter.h
+++ b/HTMLKit/HTMLNodeFilter.h
@@ -26,6 +26,9 @@ typedef NS_OPTIONS(unsigned long, HTMLNodeFilterShowOptions)
HTMLNodeFilterShowDocumentFragment = 0x400
};
+
+#pragma mark - Node Filter
+
@class HTMLNode;
@protocol HTMLNodeFilter
@@ -34,8 +37,20 @@ typedef NS_OPTIONS(unsigned long, HTMLNodeFilterShowOptions)
@end
+#pragma mark - Block Filter
+
@interface HTMLNodeFilterBlock : NSObject
+ (instancetype)filterWithBlock:(HTMLNodeFilterValue (^)(HTMLNode *node))block;
@end
+
+#pragma mark - CSS Selector Filter
+
+@class CSSSelector;
+
+@interface HTMLSelectorNodeFilter : NSObject
+
++ (instancetype)filterWithSelector:(CSSSelector *)selector;
+
+@end
diff --git a/HTMLKit/HTMLNodeFilter.m b/HTMLKit/HTMLNodeFilter.m
index 96e4843..a1e0d2b 100644
--- a/HTMLKit/HTMLNodeFilter.m
+++ b/HTMLKit/HTMLNodeFilter.m
@@ -7,6 +7,10 @@
//
#import "HTMLNodeFilter.h"
+#import "HTMLNode.h"
+#import "CSSSelector.h"
+
+#pragma mark - Block Filter
@interface HTMLNodeFilterBlock ()
{
@@ -40,3 +44,42 @@
}
@end
+
+#pragma mark - CSS Selector Filter
+
+@interface HTMLSelectorNodeFilter ()
+{
+ CSSSelector *_selector;
+}
+@end
+
+@implementation HTMLSelectorNodeFilter
+
++ (instancetype)filterWithSelector:(CSSSelector *)selector
+{
+ return [[self alloc] initWithSelector:selector];
+}
+
+- (instancetype)initWithSelector:(CSSSelector *)selector
+{
+ self = [super init];
+ if (self) {
+ _selector = selector;
+ }
+ return self;
+}
+
+- (HTMLNodeFilterValue)acceptNode:(HTMLNode *)node
+{
+ if (node.nodeType != HTMLNodeElement) {
+ return HTMLNodeFilterSkip;
+ }
+
+ if ([_selector acceptElement:node.asElement]) {
+ return HTMLNodeFilterAccept;
+ }
+
+ return HTMLNodeFilterSkip;
+}
+
+@end
diff --git a/HTMLKit/NSCharacterSet+HTMLKit.h b/HTMLKit/NSCharacterSet+HTMLKit.h
new file mode 100644
index 0000000..59dd2c7
--- /dev/null
+++ b/HTMLKit/NSCharacterSet+HTMLKit.h
@@ -0,0 +1,17 @@
+//
+// NSCharacterSet+HTMLKit.h
+// HTMLKit
+//
+// Created by Iska on 14/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+
+@interface NSCharacterSet (HTMLKit)
+
++ (instancetype)HTMLWhitespaceCharacterSet;
++ (instancetype)HTMLHexNumberCharacterSet;
++ (instancetype)CSSNthExpressionCharacterSet;
+
+@end
diff --git a/HTMLKit/NSCharacterSet+HTMLKit.m b/HTMLKit/NSCharacterSet+HTMLKit.m
new file mode 100644
index 0000000..8ad588c
--- /dev/null
+++ b/HTMLKit/NSCharacterSet+HTMLKit.m
@@ -0,0 +1,43 @@
+//
+// NSCharacterSet+HTMLKit.m
+// HTMLKit
+//
+// Created by Iska on 14/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import "NSCharacterSet+HTMLKit.h"
+
+@implementation NSCharacterSet (HTMLKit)
+
++ (instancetype)HTMLWhitespaceCharacterSet
+{
+ static NSCharacterSet *set = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ set = [NSCharacterSet characterSetWithCharactersInString:@" \t\n\r\f"];
+ });
+ return set;
+}
+
++ (instancetype)HTMLHexNumberCharacterSet
+{
+ static NSCharacterSet *set = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ set = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"];
+ });
+ return set;
+}
+
++ (instancetype)CSSNthExpressionCharacterSet
+{
+ static NSCharacterSet *set = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ set = [NSCharacterSet characterSetWithCharactersInString:@" 0123456789nN-+"];
+ });
+ return set;
+}
+
+@end
diff --git a/HTMLKitTests/CSSAttributeSelectorTests.m b/HTMLKitTests/CSSAttributeSelectorTests.m
new file mode 100644
index 0000000..8811dff
--- /dev/null
+++ b/HTMLKitTests/CSSAttributeSelectorTests.m
@@ -0,0 +1,238 @@
+//
+// CSSAttributeSelectorTests.m
+// HTMLKit
+//
+// Created by Iska on 14/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSSelectors.h"
+#import "HTMLDOM.h"
+
+@interface CSSAttributeSelectorTests : XCTestCase
+{
+ HTMLElement *_element;
+}
+@end
+
+@implementation CSSAttributeSelectorTests
+
+- (void)setUp
+{
+ [super setUp];
+ _element = [[HTMLElement alloc] initWithTagName:@"div"];
+}
+
+- (void)testAttributeExists
+{
+ CSSSelector *selector = hasAttributeSelector(@"attr");
+
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"class"] = @"class";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"value 2";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"value 3";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ [_element.attributes removeObjectForKey:@"attr"];
+ XCTAssertEqual([selector acceptElement:_element], NO);
+}
+
+- (void)testAttributeExactMatch
+{
+ CSSSelector *selector = attributeSelector(CSSAttributeSelectorExactMatch, @"attr", @"value");
+
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"class"] = @"class";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value 1";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value 2";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value 3";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ [_element.attributes removeObjectForKey:@"attr"];
+ XCTAssertEqual([selector acceptElement:_element], NO);
+}
+
+- (void)testAttributeIncludes
+{
+ CSSSelector *selector = attributeSelector(CSSAttributeSelectorIncludes, @"attr", @"value");
+
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"class"] = @"class";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"a b value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"a value b";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ [_element.attributes removeObjectForKey:@"attr"];
+ XCTAssertEqual([selector acceptElement:_element], NO);
+}
+
+- (void)testAttributeBegins
+{
+ CSSSelector *selector = attributeSelector(CSSAttributeSelectorBegins, @"attr", @"value");
+
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"class"] = @"class";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"val";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"a value";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"values";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ [_element.attributes removeObjectForKey:@"attr"];
+ XCTAssertEqual([selector acceptElement:_element], NO);
+}
+
+- (void)testAttributeEnds
+{
+ CSSSelector *selector = attributeSelector(CSSAttributeSelectorEnds, @"attr", @"value");
+
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"class"] = @"class";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"val";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"a value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"some-value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ [_element.attributes removeObjectForKey:@"attr"];
+ XCTAssertEqual([selector acceptElement:_element], NO);
+}
+
+- (void)testAttributeContains
+{
+ CSSSelector *selector = attributeSelector(CSSAttributeSelectorContains, @"attr", @"value");
+
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"class"] = @"class";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"val";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"value";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"a value b";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"some-values";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ [_element.attributes removeObjectForKey:@"attr"];
+ XCTAssertEqual([selector acceptElement:_element], NO);
+}
+
+- (void)testAttributeHyphen
+{
+ CSSSelector *selector = attributeSelector(CSSAttributeSelectorHyphen, @"attr", @"top");
+
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"class"] = @"class";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"top_text";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"toptext";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+
+ _element[@"attr"] = @"top";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"top-text";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ [_element.attributes removeObjectForKey:@"attr"];
+ XCTAssertEqual([selector acceptElement:_element], NO);
+}
+
+- (void)testAttributeNot
+{
+ CSSSelector *selector = attributeSelector(CSSAttributeSelectorNot, @"attr", @"value");
+
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"class"] = @"class";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"top_text";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"toptext";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"top";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"top-text";
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ [_element.attributes removeObjectForKey:@"attr"];
+ XCTAssertEqual([selector acceptElement:_element], YES);
+
+ _element[@"attr"] = @"value";
+ XCTAssertEqual([selector acceptElement:_element], NO);
+}
+
+- (void)testIdSelector
+{
+ _element[@"id"] = @"id";
+ XCTAssertEqual([idSelector(@"id") acceptElement:_element], YES);
+ XCTAssertEqual([idSelector(@"other id") acceptElement:_element], NO);
+}
+
+- (void)testClassSelector
+{
+ _element[@"class"] = @"class";
+ XCTAssertEqual([classSelector(@"class") acceptElement:_element], YES);
+ XCTAssertEqual([classSelector(@"other class") acceptElement:_element], NO);
+}
+
+@end
diff --git a/HTMLKitTests/CSSCombinatorSelectorTests.m b/HTMLKitTests/CSSCombinatorSelectorTests.m
new file mode 100644
index 0000000..4296068
--- /dev/null
+++ b/HTMLKitTests/CSSCombinatorSelectorTests.m
@@ -0,0 +1,116 @@
+//
+// CSSCombinatorSelectorTests.m
+// HTMLKit
+//
+// Created by Iska on 23/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSSelectors.h"
+#import "HTMLParser.h"
+#import "HTMLDOM.h"
+
+@interface CSSCombinatorSelectorTests : XCTestCase
+{
+ HTMLDocument *_document;
+}
+@end
+
+@implementation CSSCombinatorSelectorTests
+
+- (void)setUp
+{
+ [super setUp];
+
+ /*
+ |
+ |
+ | A paragraphA span
+ |
+ |
+ |
+ |
+ | Span in table
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ */
+ _document = [[[HTMLParser alloc] initWithString:@""
+ ""
+ "A paragraphA span "
+ "
"
+ ""
+ ""
+ "Span in table "
+ " "
+ " "
+ " "
+ " "
+ "
"
+ ""
+ ""
+ ""
+ ""] document];
+}
+
+-(void)testChildOfElementCombinator
+{
+ NSArray *elements = [_document elementsMatchingSelector:childOfElementSelector(typeSelector(@"body"))];
+ NSArray *expected = @[@"p1", @"inner-div1", @"inner-div2"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_document elementsMatchingSelector:childOfElementSelector(typeSelector(@"p"))];
+ expected = @[@"span1", @"table"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+-(void)testDescendantOfElementCombinator
+{
+ NSArray *elements = [_document elementsMatchingSelector:descendantOfElementSelector(typeSelector(@"p"))];
+ NSArray *expected = @[@"span1", @"table", @"tbody", @"tr1", @"td", @"span2", @"tr2", @"tr3"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_document elementsMatchingSelector:descendantOfElementSelector(typeSelector(@"table"))];
+ expected = @[@"tbody", @"tr1", @"td", @"span2", @"tr2", @"tr3"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+-(void)testAdjacentSiblingCombinator
+{
+ NSArray *elements = [_document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"tr"))];
+ NSArray *expected = @[@"tr2", @"tr3"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"p"))];
+ expected = @[@"inner-div1"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+-(void)testGeneralSiblingCombinator
+{
+ NSArray *elements = [_document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"tr"))];
+ NSArray *expected = @[@"tr2", @"tr3"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
+ expected = @[@"inner-div1", @"inner-div2"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+@end
diff --git a/HTMLKitTests/CSSNThExpressionSelectorTests.m b/HTMLKitTests/CSSNThExpressionSelectorTests.m
new file mode 100644
index 0000000..b107eeb
--- /dev/null
+++ b/HTMLKitTests/CSSNThExpressionSelectorTests.m
@@ -0,0 +1,352 @@
+//
+// CSSNThExpressionSelectorTests.m
+// HTMLKit
+//
+// Created by Iska on 21/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSSelectors.h"
+#import "HTMLParser.h"
+#import "HTMLDOM.h"
+
+@interface CSSNThExpressionSelectorTests : XCTestCase
+{
+ HTMLDocument *_childTree;
+ HTMLDocument *_typeTree;
+}
+@end
+
+@implementation CSSNThExpressionSelectorTests
+
+- (void)setUp
+{
+ [super setUp];
+
+ /*
+ |
+ |
+ | 11
+ | 12
+ | 13
+ | 14
+ |
+ |
+ |
+ |
+ |
+ | 31
+ | 32
+ | 33
+ | 34
+ | 35
+ | 36
+ |
+ |
+ */
+ _childTree = [[[HTMLParser alloc] initWithString:@""
+ "11 "
+ "12 "
+ "13 "
+ "14 "
+ "
"
+ ""
+ ""
+ "31 "
+ "32 "
+ "33 "
+ "34 "
+ "35 "
+ "36 "
+ "
"] document];
+
+ /*
+ |
+ | s11
+ | s12
+ | s13
+ | b1
+ |
+ |
+ | b2
+ | s21
+ | s22
+ | s23
+ |
+ |
+ | s31
+ | b3
+ | s32
+ | b4
+ | s33
+ | b5
+ | s34
+ |
+ */
+ _typeTree = [[[HTMLParser alloc] initWithString:@""
+ "s11 "
+ "s12 "
+ "s13 "
+ "b1 "
+ "
"
+ ""
+ "b2 "
+ "s21 "
+ "s22 "
+ "s23 "
+ "
"
+ ""
+ "s31 "
+ "b3 "
+ "s32 "
+ "b4 "
+ "s33 "
+ "b5 "
+ "s34 "
+ "
"] document];
+}
+
+- (void)testOddSelector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), oddSelector()])];
+ NSArray *expected = @[@"11", @"13", @"21", @"31", @"33", @"35"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)testEvenSelector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), evenSlector()])];
+ NSArray *expected = @[@"12", @"14", @"32", @"34", @"36"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)testFirstChildSelector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), firstChildSelector()])];
+ NSArray *expected = @[@"11", @"21", @"31"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)testLastChildSelector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), lastChildSelector()])];
+ NSArray *expected = @[@"14", @"21", @"36"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)testFirstOfTypeSelector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), firstOfTypeSelector()])];
+ NSArray *expected = @[@"s11", @"s21", @"s31"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)testLastOfTypeSelector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), lastOfTypeSelector()])];
+ NSArray *expected = @[@"s13", @"s23", @"s34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)testOnlyChildSelector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), onlyChildSelector()])];
+ NSArray *expected = @[@"21"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)testOnlyOfTypeSelector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), onlyOfTypeSelector()])];
+ NSArray *expected = @[];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), onlyOfTypeSelector()])];
+ expected = @[];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"b"), onlyOfTypeSelector()])];
+ expected = @[@"b1", @"b2"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+#pragma mark - Nth-Child Selector
+
+- (void)test_NthChild_B_Selector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(0, 1))])];
+ NSArray *expected = @[@"11", @"21", @"31"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(0, 2))])];
+ expected = @[@"12", @"32"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(0, 3))])];
+ expected = @[@"13", @"33"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)test_NthChild_An_Selector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(2, 0))])];
+ NSArray *expected = @[@"12", @"14", @"32", @"34", @"36"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(3, 0))])];
+ expected = @[@"13", @"33", @"36"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(4, 0))])];
+ expected = @[@"14", @"34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)test_NthChild_An_B_Selector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(3, 1))])];
+ NSArray *expected = @[@"11", @"14", @"21", @"31", @"34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthChildSelector(CSSNthExpressionMake(3, 2))])];
+ expected = @[@"12", @"32", @"35"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+#pragma mark - Nth-Last-Child Selector
+
+- (void)test_NthLastChild_B_Selector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(0, 1))])];
+ NSArray *expected = @[@"14", @"21", @"36"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(0, 2))])];
+ expected = @[@"13", @"35"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(0, 3))])];
+ expected = @[@"12", @"34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)test_NthLastChild_An_Selector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(2, 0))])];
+ NSArray *expected = @[@"11", @"13", @"31", @"33", @"35"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(3, 0))])];
+ expected = @[@"12", @"31", @"34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(4, 0))])];
+ expected = @[@"11", @"33"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)test_NthLastChild_An_B_Selector
+{
+ NSArray *elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(3, 1))])];
+ NSArray *expected = @[@"11", @"14", @"21", @"33", @"36"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_childTree elementsMatchingSelector:allOf(@[typeSelector(@"tr"), nthLastChildSelector(CSSNthExpressionMake(3, 2))])];
+ expected = @[@"13", @"32", @"35"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+#pragma mark - Nth-Of-Type Selector
+
+- (void)test_NthOfType_B_Selector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(0, 1))])];
+ NSArray *expected = @[@"s11", @"s21", @"s31"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(0, 2))])];
+ expected = @[@"s12", @"s22", @"s32"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(0, 3))])];
+ expected = @[@"s13", @"s23", @"s33"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)test_NthOfType_An_Selector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(2, 0))])];
+ NSArray *expected = @[@"s12", @"s22", @"s32", @"s34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(3, 0))])];
+ expected = @[@"s13", @"s23", @"s33"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(4, 0))])];
+ expected = @[@"s34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)test_NthOfType_An_B_Selector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(2, 1))])];
+ NSArray *expected = @[@"s11", @"s13", @"s21", @"s23", @"s31", @"s33"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthOfTypeSelector(CSSNthExpressionMake(2, 2))])];
+ expected = @[@"s12", @"s22", @"s32", @"s34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+#pragma mark - Nth-Last-Of-Type Selector
+
+- (void)test_NthLastOfType_B_Selector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(0, 1))])];
+ NSArray *expected = @[@"s13", @"s23", @"s34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(0, 2))])];
+ expected = @[@"s12", @"s22", @"s33"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(0, 3))])];
+ expected = @[@"s11", @"s21", @"s32"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)test_NthLastOfType_An_Selector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(2, 0))])];
+ NSArray *expected = @[@"s12", @"s22", @"s31", @"s33"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(3, 0))])];
+ expected = @[@"s11", @"s21", @"s32"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(4, 0))])];
+ expected = @[@"s31"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+- (void)test_NthLastOfType_An_B_Selector
+{
+ NSArray *elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(2, 1))])];
+ NSArray *expected = @[@"s11", @"s13", @"s21", @"s23", @"s32", @"s34"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+
+ elements = [_typeTree elementsMatchingSelector:allOf(@[typeSelector(@"span"), nthLastOfTypeSelector(CSSNthExpressionMake(2, 2))])];
+ expected = @[@"s12", @"s22", @"s31", @"s33"];
+ XCTAssertEqualObjects([elements valueForKeyPath:@"attributes.id"], expected);
+}
+
+@end
diff --git a/HTMLKitTests/CSSNthExpressionsParserTests.m b/HTMLKitTests/CSSNthExpressionsParserTests.m
new file mode 100644
index 0000000..6a811e0
--- /dev/null
+++ b/HTMLKitTests/CSSNthExpressionsParserTests.m
@@ -0,0 +1,158 @@
+//
+// CSSNthExpressionsParserTests.m
+// HTMLKit
+//
+// Created by Iska on 10/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSNthExpressionParser.h"
+
+@interface CSSNthExpressionsParserTests : XCTestCase
+
+@end
+
+@implementation CSSNthExpressionsParserTests
+
+- (void)testOddEvenExpression
+{
+ CSSNthExpression odd = [CSSNthExpressionParser parseExpression:@"odd"];
+ XCTAssertEqual(odd.an, 2);
+ XCTAssertEqual(odd.b, 1);
+
+ odd = [CSSNthExpressionParser parseExpression:@" odd"];
+ XCTAssertEqual(odd.an, 2);
+ XCTAssertEqual(odd.b, 1);
+
+ odd = [CSSNthExpressionParser parseExpression:@"odd "];
+ XCTAssertEqual(odd.an, 2);
+ XCTAssertEqual(odd.b, 1);
+
+ odd = [CSSNthExpressionParser parseExpression:@" odd "];
+ XCTAssertEqual(odd.an, 2);
+ XCTAssertEqual(odd.b, 1);
+
+ CSSNthExpression even = [CSSNthExpressionParser parseExpression:@"even"];
+ XCTAssertEqual(even.an, 2);
+ XCTAssertEqual(even.b, 0);
+
+ even = [CSSNthExpressionParser parseExpression:@" even"];
+ XCTAssertEqual(even.an, 2);
+ XCTAssertEqual(even.b, 0);
+
+ even = [CSSNthExpressionParser parseExpression:@"even "];
+ XCTAssertEqual(even.an, 2);
+ XCTAssertEqual(even.b, 0);
+
+ even = [CSSNthExpressionParser parseExpression:@" even "];
+ XCTAssertEqual(even.an, 2);
+ XCTAssertEqual(even.b, 0);
+}
+
+- (void)test_B_Expression
+{
+ CSSNthExpression expression = [CSSNthExpressionParser parseExpression:@"1"];
+ XCTAssertEqual(expression.an, 0);
+ XCTAssertEqual(expression.b, 1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"+1"];
+ XCTAssertEqual(expression.an, 0);
+ XCTAssertEqual(expression.b, 1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"-1"];
+ XCTAssertEqual(expression.an, 0);
+ XCTAssertEqual(expression.b, -1);
+
+ expression = [CSSNthExpressionParser parseExpression:@" -1"];
+ XCTAssertEqual(expression.an, 0);
+ XCTAssertEqual(expression.b, -1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"+1 "];
+ XCTAssertEqual(expression.an, 0);
+ XCTAssertEqual(expression.b, 1);
+
+ expression = [CSSNthExpressionParser parseExpression:@" +1 "];
+ XCTAssertEqual(expression.an, 0);
+ XCTAssertEqual(expression.b, 1);
+}
+
+- (void)test_AN_Expression
+{
+ CSSNthExpression expression = [CSSNthExpressionParser parseExpression:@"n"];
+ XCTAssertEqual(expression.an, 1);
+ XCTAssertEqual(expression.b, 0);
+
+ expression = [CSSNthExpressionParser parseExpression:@"+n"];
+ XCTAssertEqual(expression.an, 1);
+ XCTAssertEqual(expression.b, 0);
+
+ expression = [CSSNthExpressionParser parseExpression:@"2n"];
+ XCTAssertEqual(expression.an, 2);
+ XCTAssertEqual(expression.b, 0);
+
+ expression = [CSSNthExpressionParser parseExpression:@"+2n"];
+ XCTAssertEqual(expression.an, 2);
+ XCTAssertEqual(expression.b, 0);
+
+ expression = [CSSNthExpressionParser parseExpression:@"-n"];
+ XCTAssertEqual(expression.an, -1);
+ XCTAssertEqual(expression.b, 0);
+
+ expression = [CSSNthExpressionParser parseExpression:@"-2n"];
+ XCTAssertEqual(expression.an, -2);
+ XCTAssertEqual(expression.b, 0);
+}
+
+- (void)test_AN_B_Expression
+{
+ CSSNthExpression expression = [CSSNthExpressionParser parseExpression:@"2n+1"];
+ XCTAssertEqual(expression.an, 2);
+ XCTAssertEqual(expression.b, 1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"+2n+1"];
+ XCTAssertEqual(expression.an, 2);
+ XCTAssertEqual(expression.b, 1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"-2n+1"];
+ XCTAssertEqual(expression.an, -2);
+ XCTAssertEqual(expression.b, 1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"2n-1"];
+ XCTAssertEqual(expression.an, 2);
+ XCTAssertEqual(expression.b, -1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"-2n-1"];
+ XCTAssertEqual(expression.an, -2);
+ XCTAssertEqual(expression.b, -1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"n-1"];
+ XCTAssertEqual(expression.an, 1);
+ XCTAssertEqual(expression.b, -1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"-n-1"];
+ XCTAssertEqual(expression.an, -1);
+ XCTAssertEqual(expression.b, -1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"+n-1"];
+ XCTAssertEqual(expression.an, 1);
+ XCTAssertEqual(expression.b, -1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"+2n + 1"];
+ XCTAssertEqual(expression.an, 2);
+ XCTAssertEqual(expression.b, 1);
+
+ expression = [CSSNthExpressionParser parseExpression:@"-2n + 1"];
+ XCTAssertEqual(expression.an, -2);
+ XCTAssertEqual(expression.b, 1);
+
+ expression = [CSSNthExpressionParser parseExpression:@" 2n - 1"];
+ XCTAssertEqual(expression.an, 2);
+ XCTAssertEqual(expression.b, -1);
+
+ expression = [CSSNthExpressionParser parseExpression:@" -2n -1 "];
+ XCTAssertEqual(expression.an, -2);
+ XCTAssertEqual(expression.b, -1);
+}
+
+@end
diff --git a/HTMLKitTests/CSSSelectorParserTests.m b/HTMLKitTests/CSSSelectorParserTests.m
new file mode 100644
index 0000000..1966e44
--- /dev/null
+++ b/HTMLKitTests/CSSSelectorParserTests.m
@@ -0,0 +1,104 @@
+//
+// CSSSelectorParserTests.m
+// HTMLKit
+//
+// Created by Iska on 23/10/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import
+#import "CSSSelectorTest.h"
+#import "CSSSelectorParser.h"
+
+@interface CSSSelectorParserTests : XCTestCase
+@property (nonatomic, strong) CSSSelectorTest *testCase;
+@end
+
+@implementation CSSSelectorParserTests
+
++ (XCTestSuite *)defaultTestSuite
+{
+ XCTestSuite *suite = [[XCTestSuite alloc] initWithName:NSStringFromClass(self)];
+
+ NSArray *tests = [CSSSelectorTest loadCSSSelectorTests];
+ for (CSSSelectorTest *test in tests) {
+ [self addSelectorTest:test toTestSuite:suite];
+ }
+
+ return suite;
+}
+
++ (void)addSelectorTest:(CSSSelectorTest *)selectorTest toTestSuite:(XCTestSuite *)suite
+{
+ NSArray *allInvocations = [self testInvocations];
+ for (NSInvocation *invocation in allInvocations) {
+ XCTestCase *testCase = [[self alloc] initWithInvocation:invocation
+ testCase:selectorTest];
+ [suite addTest:testCase];
+ }
+}
+
+#pragma mark - Instance
+
+- (instancetype)initWithInvocation:(NSInvocation *)invocation
+ testCase:(CSSSelectorTest *)testCase
+{
+ self = [super initWithInvocation:invocation];
+ if (self) {
+ _testCase = testCase;
+ }
+ return self;
+}
+
+- (NSString *)name
+{
+ NSInvocation *invocation = [self invocation];
+ NSString *title = self.testCase.testName.stringByDeletingPathExtension;
+ return [NSString stringWithFormat:@"-[%@ %@_%@]", self.class, NSStringFromSelector(invocation.selector), title];
+}
+
+- (NSString *)description
+{
+ return self.name;
+}
+
+#pragma mark - Tests
+
+- (void)testParser
+{
+ for (NSDictionary *testDescription in self.testCase.selectors) {
+ NSString *selectorString = testDescription[@"selector"];
+ NSArray *expectedMatches = testDescription[@"match"];
+ NSNumber *expectedError = testDescription[@"error"];
+ HTMLElement *testDOM = self.testCase.testDOM;
+
+ NSError *error = nil;
+ CSSSelector *parsedSelector = [CSSSelectorParser parseSelector:selectorString error:&error];
+
+ if (expectedError) {
+ XCTAssertNotNil(error);
+ XCTAssertNil(parsedSelector);
+
+ NSUInteger errorLocation = [error.userInfo[CSSSelectorErrorLocationKey] unsignedIntegerValue];
+ XCTAssertEqual(errorLocation, expectedError.unsignedIntegerValue);
+ } else {
+ XCTAssertNil(error);
+ XCTAssertNotNil(parsedSelector);
+
+ NSArray *matchedElements = [testDOM elementsMatchingSelector:parsedSelector];
+ NSArray *matchedIds = [matchedElements valueForKeyPath:@"attributes.id"];
+
+ NSString *message = [NSString stringWithFormat:@"CSS test: \'%@\'\nInput:\n%@\nDOM:\n%@\nExpected:\n%@\nActual:\n%@\n",
+ self.name,
+ selectorString,
+ testDOM.outerHTML,
+ expectedMatches,
+ matchedIds];
+
+ XCTAssertEqualObjects(matchedIds, expectedMatches, @"IDs mismatch:\n%@", message);
+ }
+ }
+}
+
+@end
diff --git a/HTMLKitTests/CSSSelectorTest.h b/HTMLKitTests/CSSSelectorTest.h
new file mode 100644
index 0000000..6bb9821
--- /dev/null
+++ b/HTMLKitTests/CSSSelectorTest.h
@@ -0,0 +1,21 @@
+//
+// CSSSelectorTest.h
+// HTMLKit
+//
+// Created by Iska on 22/11/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import
+
+@class HTMLElement;
+
+@interface CSSSelectorTest : NSObject
+
+@property (nonatomic, copy) NSString *testName;
+@property (nonatomic, strong) NSArray *selectors;
+@property (nonatomic, strong) HTMLElement *testDOM;
+
++ (NSArray *)loadCSSSelectorTests;
+
+@end
diff --git a/HTMLKitTests/CSSSelectorTest.m b/HTMLKitTests/CSSSelectorTest.m
new file mode 100644
index 0000000..a85191e
--- /dev/null
+++ b/HTMLKitTests/CSSSelectorTest.m
@@ -0,0 +1,60 @@
+//
+// CSSSelectorTest.m
+// HTMLKit
+//
+// Created by Iska on 22/11/15.
+// Copyright © 2015 BrainCookie. All rights reserved.
+//
+
+#import "CSSSelectorTest.h"
+#import "HTMLParser.h"
+#import "HTMLDocument.h"
+#import "HTMLElement.h"
+#import "CSSSelectors.h"
+
+static NSString * const CSSTests = @"css-tests";
+
+@implementation CSSSelectorTest
+
++ (NSArray *)loadCSSSelectorTests
+{
+ NSString *path = [[NSBundle bundleForClass:self.class] resourcePath];
+ path = [path stringByAppendingPathComponent:CSSTests];
+
+ NSMutableArray *tests = [NSMutableArray array];
+ NSArray *testFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
+
+ for (NSString *testFile in testFiles) {
+ if (![testFile.pathExtension isEqualToString:@"html"]) {
+ continue;
+ }
+
+ NSString *testFilePath = [path stringByAppendingPathComponent:testFile];
+ CSSSelectorTest *test = [CSSSelectorTest testWithFileAtPath:testFilePath];
+ [tests addObject:test];
+ }
+
+ return tests;
+}
+
++ (instancetype)testWithFileAtPath:(NSString *)filePath
+{
+ NSString *testName = filePath.lastPathComponent.stringByDeletingPathExtension;
+
+ NSString *html = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
+
+ HTMLDocument *document = [HTMLDocument documentWithString:html];
+
+ HTMLElement *domElement = [document firstElementMatchingSelector:idSelector(@"testDOM")];
+ HTMLElement *scriptElement = [document firstElementMatchingSelector:idSelector(@"selectors")];
+ NSData *data = [scriptElement.textContent dataUsingEncoding:NSUTF8StringEncoding];
+ NSArray *selectors = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
+
+ CSSSelectorTest *instance = [CSSSelectorTest new];
+ instance.testName = testName;
+ instance.selectors = selectors;
+ instance.testDOM = domElement;
+ return instance;
+}
+
+@end
diff --git a/HTMLKitTests/CSSTypeSelectorTests.m b/HTMLKitTests/CSSTypeSelectorTests.m
new file mode 100644
index 0000000..58b26a8
--- /dev/null
+++ b/HTMLKitTests/CSSTypeSelectorTests.m
@@ -0,0 +1,43 @@
+//
+// CSSTypeSelectorTests.m
+// HTMLKit
+//
+// Created by Iska on 14/05/15.
+// Copyright (c) 2015 BrainCookie. All rights reserved.
+//
+
+#import
+#import "CSSSelectors.h"
+#import "HTMLDOM.h"
+#import "CSSSelectorParser.h"
+
+@interface CSSTypeSelectorTests : XCTestCase
+
+@end
+
+@implementation CSSTypeSelectorTests
+
+- (void)testUniversalSelector
+{
+ CSSSelector *selector = universalSelector();
+
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:nil]], YES);
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@""]], YES);
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"div"]], YES);
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"p"]], YES);
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"any other name"]], YES);
+}
+
+- (void)testTypeSelector
+{
+ CSSSelector *selector = typeSelector(@"div");
+
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:nil]], NO);
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@""]], NO);
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"p"]], NO);
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"any other name"]], NO);
+
+ XCTAssertEqual([selector acceptElement:[[HTMLElement alloc] initWithTagName:@"div"]], YES);
+}
+
+@end
diff --git a/HTMLKitTests/css-tests/1.html b/HTMLKitTests/css-tests/1.html
new file mode 100644
index 0000000..7a929b6
--- /dev/null
+++ b/HTMLKitTests/css-tests/1.html
@@ -0,0 +1,13 @@
+
+
+
+ The background of this list item should be green
+ The background of this second list item should be also green
+
+
The background of this paragraph should be green.
+
diff --git a/HTMLKitTests/css-tests/10.html b/HTMLKitTests/css-tests/10.html
new file mode 100644
index 0000000..4cac7d8
--- /dev/null
+++ b/HTMLKitTests/css-tests/10.html
@@ -0,0 +1,9 @@
+
+
+
This paragraph should have a green background because its title attribute ends with "bar"
+
diff --git a/HTMLKitTests/css-tests/11.html b/HTMLKitTests/css-tests/11.html
new file mode 100644
index 0000000..e324c90
--- /dev/null
+++ b/HTMLKitTests/css-tests/11.html
@@ -0,0 +1,9 @@
+
+
+
This paragraph should have a green background because its title attribute contains "bar"
+
diff --git a/HTMLKitTests/css-tests/13.html b/HTMLKitTests/css-tests/13.html
new file mode 100644
index 0000000..ff0776a
--- /dev/null
+++ b/HTMLKitTests/css-tests/13.html
@@ -0,0 +1,23 @@
+
+
+
+ This list item should have green background because its class is "t1"
+ This list item should have green background because its class is "t2"
+
+ This list item should have green background because the inner SPAN does not match SPAN.t3
+
+
+
diff --git a/HTMLKitTests/css-tests/14.html b/HTMLKitTests/css-tests/14.html
new file mode 100644
index 0000000..9746a50
--- /dev/null
+++ b/HTMLKitTests/css-tests/14.html
@@ -0,0 +1,30 @@
+
+
+
This paragraph should have a green background and a green thick solid border because it carries both classes t1 and t2.
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/144.html b/HTMLKitTests/css-tests/144.html
new file mode 100644
index 0000000..255ea3d
--- /dev/null
+++ b/HTMLKitTests/css-tests/144.html
@@ -0,0 +1,11 @@
+
+
+
+
This paragraph should have a green background.
+
+
diff --git a/HTMLKitTests/css-tests/148.html b/HTMLKitTests/css-tests/148.html
new file mode 100644
index 0000000..87d2a6a
--- /dev/null
+++ b/HTMLKitTests/css-tests/148.html
@@ -0,0 +1,9 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/149.html b/HTMLKitTests/css-tests/149.html
new file mode 100644
index 0000000..c5bdb92
--- /dev/null
+++ b/HTMLKitTests/css-tests/149.html
@@ -0,0 +1,10 @@
+
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/14b.html b/HTMLKitTests/css-tests/14b.html
new file mode 100644
index 0000000..de12356
--- /dev/null
+++ b/HTMLKitTests/css-tests/14b.html
@@ -0,0 +1,22 @@
+
+
+
This line should be green.
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/14c.html b/HTMLKitTests/css-tests/14c.html
new file mode 100644
index 0000000..26c5459
--- /dev/null
+++ b/HTMLKitTests/css-tests/14c.html
@@ -0,0 +1,19 @@
+
+
+
This line should be green.
+
This line should be green.
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/14d.html b/HTMLKitTests/css-tests/14d.html
new file mode 100644
index 0000000..ef67d2d
--- /dev/null
+++ b/HTMLKitTests/css-tests/14d.html
@@ -0,0 +1,21 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/14e.html b/HTMLKitTests/css-tests/14e.html
new file mode 100644
index 0000000..90ec981
--- /dev/null
+++ b/HTMLKitTests/css-tests/14e.html
@@ -0,0 +1,19 @@
+
+
+
This line should be green.
+
This line should be green.
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/15.html b/HTMLKitTests/css-tests/15.html
new file mode 100644
index 0000000..40ccf78
--- /dev/null
+++ b/HTMLKitTests/css-tests/15.html
@@ -0,0 +1,27 @@
+
+
+
+ This list item should have a green background. because its ID is "t1"
+ This list item should have a green background. because its ID is "t2"
+
+ This list item should have a green background. because the inner SPAN does not match "#t4"
+
+
+
diff --git a/HTMLKitTests/css-tests/150.html b/HTMLKitTests/css-tests/150.html
new file mode 100644
index 0000000..209522a
--- /dev/null
+++ b/HTMLKitTests/css-tests/150.html
@@ -0,0 +1,11 @@
+
+
+
+
This line should have a green background.
+
(Note: This test is based on unpublished errata.)
+
diff --git a/HTMLKitTests/css-tests/151.html b/HTMLKitTests/css-tests/151.html
new file mode 100644
index 0000000..5f267e2
--- /dev/null
+++ b/HTMLKitTests/css-tests/151.html
@@ -0,0 +1,10 @@
+
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/152.html b/HTMLKitTests/css-tests/152.html
new file mode 100644
index 0000000..83c251f
--- /dev/null
+++ b/HTMLKitTests/css-tests/152.html
@@ -0,0 +1,10 @@
+
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/154.html b/HTMLKitTests/css-tests/154.html
new file mode 100644
index 0000000..ad60662
--- /dev/null
+++ b/HTMLKitTests/css-tests/154.html
@@ -0,0 +1,10 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/155.html b/HTMLKitTests/css-tests/155.html
new file mode 100644
index 0000000..5c6b222
--- /dev/null
+++ b/HTMLKitTests/css-tests/155.html
@@ -0,0 +1,10 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/155a.html b/HTMLKitTests/css-tests/155a.html
new file mode 100644
index 0000000..df6b43b
--- /dev/null
+++ b/HTMLKitTests/css-tests/155a.html
@@ -0,0 +1,9 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/155b.html b/HTMLKitTests/css-tests/155b.html
new file mode 100644
index 0000000..7cc185e
--- /dev/null
+++ b/HTMLKitTests/css-tests/155b.html
@@ -0,0 +1,9 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/155c.html b/HTMLKitTests/css-tests/155c.html
new file mode 100644
index 0000000..12ecc98
--- /dev/null
+++ b/HTMLKitTests/css-tests/155c.html
@@ -0,0 +1,9 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/155d.html b/HTMLKitTests/css-tests/155d.html
new file mode 100644
index 0000000..bbcf40b
--- /dev/null
+++ b/HTMLKitTests/css-tests/155d.html
@@ -0,0 +1,9 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/156.html b/HTMLKitTests/css-tests/156.html
new file mode 100644
index 0000000..2349293
--- /dev/null
+++ b/HTMLKitTests/css-tests/156.html
@@ -0,0 +1,10 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/157.html b/HTMLKitTests/css-tests/157.html
new file mode 100644
index 0000000..75df363
--- /dev/null
+++ b/HTMLKitTests/css-tests/157.html
@@ -0,0 +1,10 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/158.html b/HTMLKitTests/css-tests/158.html
new file mode 100644
index 0000000..51ae49a
--- /dev/null
+++ b/HTMLKitTests/css-tests/158.html
@@ -0,0 +1,10 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/15b.html b/HTMLKitTests/css-tests/15b.html
new file mode 100644
index 0000000..6420813
--- /dev/null
+++ b/HTMLKitTests/css-tests/15b.html
@@ -0,0 +1,22 @@
+
+
+
This line should be green.
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/16.html b/HTMLKitTests/css-tests/16.html
new file mode 100644
index 0000000..dc2ffbe
--- /dev/null
+++ b/HTMLKitTests/css-tests/16.html
@@ -0,0 +1,15 @@
+
+
diff --git a/HTMLKitTests/css-tests/160.html b/HTMLKitTests/css-tests/160.html
new file mode 100644
index 0000000..e8400a3
--- /dev/null
+++ b/HTMLKitTests/css-tests/160.html
@@ -0,0 +1,10 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/170.html b/HTMLKitTests/css-tests/170.html
new file mode 100644
index 0000000..7ed4a5e
--- /dev/null
+++ b/HTMLKitTests/css-tests/170.html
@@ -0,0 +1,9 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/170a.html b/HTMLKitTests/css-tests/170a.html
new file mode 100644
index 0000000..d4de3c7
--- /dev/null
+++ b/HTMLKitTests/css-tests/170a.html
@@ -0,0 +1,9 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/170b.html b/HTMLKitTests/css-tests/170b.html
new file mode 100644
index 0000000..6829742
--- /dev/null
+++ b/HTMLKitTests/css-tests/170b.html
@@ -0,0 +1,9 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/170c.html b/HTMLKitTests/css-tests/170c.html
new file mode 100644
index 0000000..d063fb4
--- /dev/null
+++ b/HTMLKitTests/css-tests/170c.html
@@ -0,0 +1,9 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/170d.html b/HTMLKitTests/css-tests/170d.html
new file mode 100644
index 0000000..1487f3a
--- /dev/null
+++ b/HTMLKitTests/css-tests/170d.html
@@ -0,0 +1,9 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/175a.html b/HTMLKitTests/css-tests/175a.html
new file mode 100644
index 0000000..40f60bb
--- /dev/null
+++ b/HTMLKitTests/css-tests/175a.html
@@ -0,0 +1,10 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/175b.html b/HTMLKitTests/css-tests/175b.html
new file mode 100644
index 0000000..264e958
--- /dev/null
+++ b/HTMLKitTests/css-tests/175b.html
@@ -0,0 +1,9 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/175c.html b/HTMLKitTests/css-tests/175c.html
new file mode 100644
index 0000000..2b39faf
--- /dev/null
+++ b/HTMLKitTests/css-tests/175c.html
@@ -0,0 +1,9 @@
+
+
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/176.html b/HTMLKitTests/css-tests/176.html
new file mode 100644
index 0000000..698d05f
--- /dev/null
+++ b/HTMLKitTests/css-tests/176.html
@@ -0,0 +1,30 @@
+
+
+
This line should be green.
+
This line should be green.
+
diff --git a/HTMLKitTests/css-tests/177b.html b/HTMLKitTests/css-tests/177b.html
new file mode 100644
index 0000000..df37984
--- /dev/null
+++ b/HTMLKitTests/css-tests/177b.html
@@ -0,0 +1,12 @@
+
+
+
+
This line should be green.
+
+
diff --git a/HTMLKitTests/css-tests/181.html b/HTMLKitTests/css-tests/181.html
new file mode 100644
index 0000000..a6ba620
--- /dev/null
+++ b/HTMLKitTests/css-tests/181.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
This line should be green.
+
This line should be green.
+
This line should be green.
+
+
+
+
diff --git a/HTMLKitTests/css-tests/183.html b/HTMLKitTests/css-tests/183.html
new file mode 100644
index 0000000..582fee1
--- /dev/null
+++ b/HTMLKitTests/css-tests/183.html
@@ -0,0 +1,25 @@
+
+
+
This text should be green.
+
This text should be green.
+
This text should be green.
+
This text should be green.
+
This text should be green.
+
This text should be green.
+
diff --git a/HTMLKitTests/css-tests/184a.html b/HTMLKitTests/css-tests/184a.html
new file mode 100644
index 0000000..cc43a72
--- /dev/null
+++ b/HTMLKitTests/css-tests/184a.html
@@ -0,0 +1,10 @@
+
+
+
This text should be green.
+
This text should be green.
+
diff --git a/HTMLKitTests/css-tests/184b.html b/HTMLKitTests/css-tests/184b.html
new file mode 100644
index 0000000..f92d416
--- /dev/null
+++ b/HTMLKitTests/css-tests/184b.html
@@ -0,0 +1,10 @@
+
+
+
This text should be green.
+
This text should be green.
+
diff --git a/HTMLKitTests/css-tests/184c.html b/HTMLKitTests/css-tests/184c.html
new file mode 100644
index 0000000..8d25e8a
--- /dev/null
+++ b/HTMLKitTests/css-tests/184c.html
@@ -0,0 +1,10 @@
+
+
+
This text should be green.
+
This text should be green.
+
diff --git a/HTMLKitTests/css-tests/184d.html b/HTMLKitTests/css-tests/184d.html
new file mode 100644
index 0000000..dfc0f97
--- /dev/null
+++ b/HTMLKitTests/css-tests/184d.html
@@ -0,0 +1,10 @@
+
+
+
This text should be green.
+
This text should be green.
+
diff --git a/HTMLKitTests/css-tests/184e.html b/HTMLKitTests/css-tests/184e.html
new file mode 100644
index 0000000..45c3433
--- /dev/null
+++ b/HTMLKitTests/css-tests/184e.html
@@ -0,0 +1,10 @@
+
+
+
This text should be green.
+
This text should be green.
+
diff --git a/HTMLKitTests/css-tests/184f.html b/HTMLKitTests/css-tests/184f.html
new file mode 100644
index 0000000..d669805
--- /dev/null
+++ b/HTMLKitTests/css-tests/184f.html
@@ -0,0 +1,10 @@
+
+
+
This text should be green.
+
This text should be green.
+
diff --git a/HTMLKitTests/css-tests/2.html b/HTMLKitTests/css-tests/2.html
new file mode 100644
index 0000000..c1efff3
--- /dev/null
+++ b/HTMLKitTests/css-tests/2.html
@@ -0,0 +1,9 @@
+
+
+
This address element should have a green background.
+
diff --git a/HTMLKitTests/css-tests/23.html b/HTMLKitTests/css-tests/23.html
new file mode 100644
index 0000000..5ffbae0
--- /dev/null
+++ b/HTMLKitTests/css-tests/23.html
@@ -0,0 +1,17 @@
+
+
+
+ A button (enabled) with green background
+
+
+
+
diff --git a/HTMLKitTests/css-tests/24.html b/HTMLKitTests/css-tests/24.html
new file mode 100644
index 0000000..4455e33
--- /dev/null
+++ b/HTMLKitTests/css-tests/24.html
@@ -0,0 +1,17 @@
+
+
+
+ A button (disabled) with green background
+
+
+
+
diff --git a/HTMLKitTests/css-tests/25.html b/HTMLKitTests/css-tests/25.html
new file mode 100644
index 0000000..11d0372
--- /dev/null
+++ b/HTMLKitTests/css-tests/25.html
@@ -0,0 +1,20 @@
+
+
+
+
+ Everything in this paragraph should have a green background
+
+
diff --git a/HTMLKitTests/css-tests/27a.html b/HTMLKitTests/css-tests/27a.html
new file mode 100644
index 0000000..f8be676
--- /dev/null
+++ b/HTMLKitTests/css-tests/27a.html
@@ -0,0 +1,61 @@
+
+
+
This line should be green (there should be no red on this page).
+
diff --git a/HTMLKitTests/css-tests/27b.html b/HTMLKitTests/css-tests/27b.html
new file mode 100644
index 0000000..3c189ed
--- /dev/null
+++ b/HTMLKitTests/css-tests/27b.html
@@ -0,0 +1,13 @@
+
+
+
This line should be green (there should be no red on this page).
+
diff --git a/HTMLKitTests/css-tests/28.html b/HTMLKitTests/css-tests/28.html
new file mode 100644
index 0000000..7769632
--- /dev/null
+++ b/HTMLKitTests/css-tests/28.html
@@ -0,0 +1,103 @@
+
+
+
+ This first list item should have a green background
+ Second list item
+ This third list item should have a green background
+ Fourth list item
+ This fifth list item should have a green background
+ Sixth list item
+
+
+ First list item
+ This second list item should have a green background
+ Third list item
+ This fourth list item should have a green background
+ Fifth list item
+ This sixth list item should have a green background
+
+
+
+
+ Green row : 1.1
+ 1.2
+ 1.3
+
+
+ Green row : 2.1
+ 2.2
+ 2.3
+
+
+ Green row : 3.1
+ 3.2
+ 3.3
+
+
+ Green row : 4.1
+ 4.2
+ 4.3
+
+
+ 5.1
+ 5.2
+ 5.3
+
+
+ 6.1
+ 6.2
+ 6.3
+
+
+
+
+
+ green cell
+ 1.2
+ 1.3
+ green cell
+ 1.5
+ 1.6
+ green cell
+ 1.8
+
+
+ green cell
+ 2.2
+ 2.3
+ green cell
+ 2.5
+ 2.6
+ green cell
+ 2.8
+
+
+ green cell
+ 3.2
+ 3.3
+ green cell
+ 3.5
+ 3.6
+ green cell
+ 3.8
+
+
+
+
diff --git a/HTMLKitTests/css-tests/29.html b/HTMLKitTests/css-tests/29.html
new file mode 100644
index 0000000..845fb64
--- /dev/null
+++ b/HTMLKitTests/css-tests/29.html
@@ -0,0 +1,103 @@
+
+
+
+ First list item
+ This second list item should have a green background
+ Third list item
+ This fourth list item should have a green background
+ Fifth list item
+ This sixth list item should have a green background
+
+
+ This first list item should have a green background
+ Second list item
+ This third list item should have a green background
+ Fourth list item
+ This fifth list item should have a green background
+ Sixth list item
+
+
+
+
+ 1.1
+ 1.2
+ 1.3
+
+
+ 2.1
+ 2.2
+ 2.3
+
+
+ Green row : 3.1
+ 3.2
+ 3.3
+
+
+ Green row : 4.1
+ 4.2
+ 4.3
+
+
+ Green row : 5.1
+ 5.2
+ 5.3
+
+
+ Green row : 6.1
+ 6.2
+ 6.3
+
+
+
+
+
+ 1.1
+ green cell
+ 1.3
+ 1.4
+ green cell
+ 1.6
+ 1.7
+ green cell
+
+
+ 2.1
+ green cell
+ 2.3
+ 2.4
+ green cell
+ 2.6
+ 2.7
+ green cell
+
+
+ 3.1
+ green cell
+ 3.3
+ 3.4
+ green cell
+ 3.6
+ 3.7
+ green cell
+
+
+
+
diff --git a/HTMLKitTests/css-tests/3.html b/HTMLKitTests/css-tests/3.html
new file mode 100644
index 0000000..c0aa613
--- /dev/null
+++ b/HTMLKitTests/css-tests/3.html
@@ -0,0 +1,18 @@
+
+
+
+ This paragraph, and all textual contents in the document, should be green.
+
+
+ This item should be green.
+
+
diff --git a/HTMLKitTests/css-tests/30.html b/HTMLKitTests/css-tests/30.html
new file mode 100644
index 0000000..9d22198
--- /dev/null
+++ b/HTMLKitTests/css-tests/30.html
@@ -0,0 +1,30 @@
+
+
+
This paragraph is here only to fill space in the DOM
+
And this address too..
+
So does this paragraph !
+
But this one should have green background
+
+ First definition term that should have green background
+ First definition that should have green background
+ Second definition term
+ Second definition
+ Third definition term
+ Third definition
+ Fourth definition term that should have green background
+ Fourth definition that should have green background
+ Fifth definition term
+ Fifth definition
+ Sixth definition term
+ Sixth definition
+
+
diff --git a/HTMLKitTests/css-tests/31.html b/HTMLKitTests/css-tests/31.html
new file mode 100644
index 0000000..57c516b
--- /dev/null
+++ b/HTMLKitTests/css-tests/31.html
@@ -0,0 +1,30 @@
+
+
+
This paragraph should have green background
+
But this address is here only to fill space in the dom..
+
So does this paragraph !
+
And so does this one too.
+
+ First definition term
+ First definition
+ Second definition term
+ Second definition
+ Third definition term that should have green background
+ Third definition that should have green background
+ Fourth definition term
+ Fourth definition
+ Fifth definition term
+ Fifth definition
+ Sixth definition term that should have green background
+ Sixth definition that should have green background
+
+
diff --git a/HTMLKitTests/css-tests/32.html b/HTMLKitTests/css-tests/32.html
new file mode 100644
index 0000000..f310ef7
--- /dev/null
+++ b/HTMLKitTests/css-tests/32.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+ green cell
+ 1.2
+ 1.3
+
+
+ green cell
+ 2.2
+ 2.3
+
+
+ green cell
+ 3.2
+ 3.3
+
+
+
+
This paragraph contains some text
+ and a span that should have a green background
+
+
diff --git a/HTMLKitTests/css-tests/33.html b/HTMLKitTests/css-tests/33.html
new file mode 100644
index 0000000..6b2d3e1
--- /dev/null
+++ b/HTMLKitTests/css-tests/33.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+ 1.1
+ 1.2
+ green cell
+
+
+ 2.1
+ 2.2
+ green cell
+
+
+ 3.1
+ 3.2
+ green cell
+
+
+
+
+ This paragraph contains a span that should
+ have a green background and some text after it.
+
diff --git a/HTMLKitTests/css-tests/34.html b/HTMLKitTests/css-tests/34.html
new file mode 100644
index 0000000..0d4952a
--- /dev/null
+++ b/HTMLKitTests/css-tests/34.html
@@ -0,0 +1,13 @@
+
+
+
This div contains 3 addresses:
+
A first address that should have a green background
+
A second address with normal background
+
A third address with normal background
+
+
diff --git a/HTMLKitTests/css-tests/35.html b/HTMLKitTests/css-tests/35.html
new file mode 100644
index 0000000..9720100
--- /dev/null
+++ b/HTMLKitTests/css-tests/35.html
@@ -0,0 +1,14 @@
+
+
+
+
A first address with normal background
+
A second address with normal background
+
A third address that should have a green background
+ This div contains 3 addresses above this sentence.
+
+
diff --git a/HTMLKitTests/css-tests/36.html b/HTMLKitTests/css-tests/36.html
new file mode 100644
index 0000000..acadf0f
--- /dev/null
+++ b/HTMLKitTests/css-tests/36.html
@@ -0,0 +1,16 @@
+
+
+
This paragraph should have normal background
+
This div contains only one paragraph
+
This paragraph should have green background
+
+
diff --git a/HTMLKitTests/css-tests/37.html b/HTMLKitTests/css-tests/37.html
new file mode 100644
index 0000000..f482dc0
--- /dev/null
+++ b/HTMLKitTests/css-tests/37.html
@@ -0,0 +1,13 @@
+
+
+
+
This paragraph should have normal background
+
But this address should have green background
+
This paragraph should have normal background
+
+
diff --git a/HTMLKitTests/css-tests/4.html b/HTMLKitTests/css-tests/4.html
new file mode 100644
index 0000000..0f69206
--- /dev/null
+++ b/HTMLKitTests/css-tests/4.html
@@ -0,0 +1,9 @@
+
+
+
This paragraph should have a green background
+
diff --git a/HTMLKitTests/css-tests/43.html b/HTMLKitTests/css-tests/43.html
new file mode 100644
index 0000000..83ac030
--- /dev/null
+++ b/HTMLKitTests/css-tests/43.html
@@ -0,0 +1,29 @@
+
+
+
+
This paragraph should have a green background
+
+
+
+
+ This paragraph should have a green background
+
+
+
+
+
+
+
+
+
+ This paragraph should be unstyled.
+
+
+
+
+
diff --git a/HTMLKitTests/css-tests/44.html b/HTMLKitTests/css-tests/44.html
new file mode 100644
index 0000000..076b4b2
--- /dev/null
+++ b/HTMLKitTests/css-tests/44.html
@@ -0,0 +1,23 @@
+
+
+
+
This paragraph should have a green background
+
+
This paragraph should have a green background
+
+
+
+
+
+
+ This paragraph should be unstyled.
+
+
+
+
+
diff --git a/HTMLKitTests/css-tests/44c.html b/HTMLKitTests/css-tests/44c.html
new file mode 100644
index 0000000..5c6007b
--- /dev/null
+++ b/HTMLKitTests/css-tests/44c.html
@@ -0,0 +1,10 @@
+
+
+
This should be unstyled.
+
This should have a green background.
+
diff --git a/HTMLKitTests/css-tests/44d.html b/HTMLKitTests/css-tests/44d.html
new file mode 100644
index 0000000..3d6e5c9
--- /dev/null
+++ b/HTMLKitTests/css-tests/44d.html
@@ -0,0 +1,10 @@
+
+
+
This should be unstyled.
+
This should have a green background.
+
diff --git a/HTMLKitTests/css-tests/45.html b/HTMLKitTests/css-tests/45.html
new file mode 100644
index 0000000..4d84ec7
--- /dev/null
+++ b/HTMLKitTests/css-tests/45.html
@@ -0,0 +1,15 @@
+
+
+
+
This paragraph should be unstyled.
+
But this one should have a green background.
+
And this one should also have a green background.
+
This address is only here to fill some space between two paragraphs.
+
This paragraph should be unstyled.
+
+
diff --git a/HTMLKitTests/css-tests/45c.html b/HTMLKitTests/css-tests/45c.html
new file mode 100644
index 0000000..ca52954
--- /dev/null
+++ b/HTMLKitTests/css-tests/45c.html
@@ -0,0 +1,10 @@
+
+
+
This should be unstyled.
+
This should have a green background.
+
diff --git a/HTMLKitTests/css-tests/46.html b/HTMLKitTests/css-tests/46.html
new file mode 100644
index 0000000..863ea22
--- /dev/null
+++ b/HTMLKitTests/css-tests/46.html
@@ -0,0 +1,15 @@
+
+
+
+
This paragraph should be unstyled.
+
But this one should have a green background
+
And this one should also have a green background
+
This address is only here to fill some space between two paragraphs
+
This paragraph should have a green background
+
+
diff --git a/HTMLKitTests/css-tests/5.html b/HTMLKitTests/css-tests/5.html
new file mode 100644
index 0000000..6ce6434
--- /dev/null
+++ b/HTMLKitTests/css-tests/5.html
@@ -0,0 +1,9 @@
+
+
+
This paragraph should have a green background because its TITLE attribute is set.
+
diff --git a/HTMLKitTests/css-tests/54.html b/HTMLKitTests/css-tests/54.html
new file mode 100644
index 0000000..4c36daf
--- /dev/null
+++ b/HTMLKitTests/css-tests/54.html
@@ -0,0 +1,19 @@
+
+
+
+
This paragraph should be in green characters.
+
This paragraph should be in green characters.
+
+ This paragraph should be in green characters.
+
+
+
diff --git a/HTMLKitTests/css-tests/55.html b/HTMLKitTests/css-tests/55.html
new file mode 100644
index 0000000..ebe5406
--- /dev/null
+++ b/HTMLKitTests/css-tests/55.html
@@ -0,0 +1,19 @@
+
+
+
+
This paragraph should be in green characters.
+
This paragraph should be in green characters.
+
+ This paragraph should be in green characters.
+
+
+
diff --git a/HTMLKitTests/css-tests/56.html b/HTMLKitTests/css-tests/56.html
new file mode 100644
index 0000000..8bd5345
--- /dev/null
+++ b/HTMLKitTests/css-tests/56.html
@@ -0,0 +1,19 @@
+
+
+
+
This paragraph should be in green characters.
+
This paragraph should be in green characters.
+
+ This paragraph should be in green characters.
+
+
+
diff --git a/HTMLKitTests/css-tests/59.html b/HTMLKitTests/css-tests/59.html
new file mode 100644
index 0000000..2c0d5ab
--- /dev/null
+++ b/HTMLKitTests/css-tests/59.html
@@ -0,0 +1,19 @@
+
+
+
+
This paragraph should be in green characters.
+
This paragraph should be in green characters.
+
+ This paragraph should be in green characters.
+
+
+
diff --git a/HTMLKitTests/css-tests/6.html b/HTMLKitTests/css-tests/6.html
new file mode 100644
index 0000000..d2ec421
--- /dev/null
+++ b/HTMLKitTests/css-tests/6.html
@@ -0,0 +1,21 @@
+
+
+
+ This line should
+ have a green background.
+
+
+
diff --git a/HTMLKitTests/css-tests/60.html b/HTMLKitTests/css-tests/60.html
new file mode 100644
index 0000000..0b74f40
--- /dev/null
+++ b/HTMLKitTests/css-tests/60.html
@@ -0,0 +1,19 @@
+
+
+
+
This paragraph should be in green characters.
+
This paragraph should be in green characters.
+
+ This paragraph should be in green characters.
+
+
+
diff --git a/HTMLKitTests/css-tests/68.html b/HTMLKitTests/css-tests/68.html
new file mode 100644
index 0000000..856c58e
--- /dev/null
+++ b/HTMLKitTests/css-tests/68.html
@@ -0,0 +1,17 @@
+
+
+
+ A button (disabled) with green background
+
+
+
+
diff --git a/HTMLKitTests/css-tests/69.html b/HTMLKitTests/css-tests/69.html
new file mode 100644
index 0000000..0e77724
--- /dev/null
+++ b/HTMLKitTests/css-tests/69.html
@@ -0,0 +1,17 @@
+
+
+
+ A button (enabled) with green background
+
+
+
+
diff --git a/HTMLKitTests/css-tests/7.html b/HTMLKitTests/css-tests/7.html
new file mode 100644
index 0000000..08bd82e
--- /dev/null
+++ b/HTMLKitTests/css-tests/7.html
@@ -0,0 +1,21 @@
+
+
+
This paragraph should have green background because CLASS contains "b"
+
+ This address should also
+ have green background because the selector in the last rule does not apply to the inner SPANs.
+
+
diff --git a/HTMLKitTests/css-tests/70.html b/HTMLKitTests/css-tests/70.html
new file mode 100644
index 0000000..596b63e
--- /dev/null
+++ b/HTMLKitTests/css-tests/70.html
@@ -0,0 +1,18 @@
+
+
+
+ Everything in this paragraph should have a green background
+
diff --git a/HTMLKitTests/css-tests/72.html b/HTMLKitTests/css-tests/72.html
new file mode 100644
index 0000000..7b4d261
--- /dev/null
+++ b/HTMLKitTests/css-tests/72.html
@@ -0,0 +1,11 @@
+
+
+
+
This paragraph should have a green background and there should be no red anywhere.
+
+
diff --git a/HTMLKitTests/css-tests/72b.html b/HTMLKitTests/css-tests/72b.html
new file mode 100644
index 0000000..c1b2cbb
--- /dev/null
+++ b/HTMLKitTests/css-tests/72b.html
@@ -0,0 +1,11 @@
+
+
+
+
This paragraph should have a green background and there should be no red anywhere.
+
+
diff --git a/HTMLKitTests/css-tests/73.html b/HTMLKitTests/css-tests/73.html
new file mode 100644
index 0000000..fbd1b61
--- /dev/null
+++ b/HTMLKitTests/css-tests/73.html
@@ -0,0 +1,103 @@
+
+
+
+ First list item
+ This second list item should have a green background
+ Third list
+ This fourth list item should have a green background
+ Fifth list item
+ This sixth list item should have a green background
+
+
+ This first list item should have a green background
+ Second list item
+ This third list item should have a green background
+ Fourth list item
+ This fifth list item should have a green background
+ Sixth list item
+
+
+
+
+ 1.1
+ 1.2
+ 1.3
+
+
+ 2.1
+ 2.2
+ 2.3
+
+
+ 3.1
+ 3.2
+ 3.3
+
+
+ 4.1
+ 4.2
+ 4.3
+
+
+ Green row : 5.1
+ 5.2
+ 5.3
+
+
+ Green row : 6.1
+ 6.2
+ 6.3
+
+
+
+
+
+ 1.1
+ green cell
+ green cell
+ 1.4
+ green cell
+ green cell
+ 1.7
+ green cell
+
+
+ 2.1
+ green cell
+ green cell
+ 2.4
+ green cell
+ green cell
+ 2.7
+ green cell
+
+
+ 3.1
+ green cell
+ green cell
+ 3.4
+ green cell
+ green cell
+ 3.7
+ green cell
+
+
+
+
diff --git a/HTMLKitTests/css-tests/74.html b/HTMLKitTests/css-tests/74.html
new file mode 100644
index 0000000..11789d9
--- /dev/null
+++ b/HTMLKitTests/css-tests/74.html
@@ -0,0 +1,103 @@
+
+
+
+ This first list item should have a green background
+ Second list item
+ This third list item should have a green background
+ Fourth list item
+ This fifth list item should have a green background
+ Sixth list item
+
+
+ First list item
+ This second list item should have a green background
+ Third list item
+ This fourth list item should have a green background
+ Fifth list item
+ This sixth list item should have a green background
+
+
+
+
+ Green row : 1.1
+ 1.2
+ 1.3
+
+
+ Green row : 2.1
+ 2.2
+ 2.3
+
+
+ 3.1
+ 3.2
+ 3.3
+
+
+ 4.1
+ 4.2
+ 4.3
+
+
+ 5.1
+ 5.2
+ 5.3
+
+
+ 6.1
+ 6.2
+ 6.3
+
+
+
+
+
+ green cell
+ 1.2
+ green cell
+ green cell
+ 1.5
+ green cell
+ green cell
+ 1.8
+
+
+ green cell
+ 2.2
+ green cell
+ green cell
+ 2.5
+ green cell
+ green cell
+ 2.8
+
+
+ green cell
+ 3.2
+ green cell
+ green cell
+ 3.5
+ green cell
+ green cell
+ 3.8
+
+
+
+
diff --git a/HTMLKitTests/css-tests/75.html b/HTMLKitTests/css-tests/75.html
new file mode 100644
index 0000000..868e05e
--- /dev/null
+++ b/HTMLKitTests/css-tests/75.html
@@ -0,0 +1,30 @@
+
+
+
This paragraph should have green background
+
And this address should be unstyled.
+
This paragraph should also have green background!
+
But this one should be unstyled again.
+
+ First definition term
+ First definition
+ Second definition term that should have green background
+ Second definition that should have green background
+ Third definition term that should have green background
+ Third definition that should have green background
+ Fourth definition term
+ Fourth definition
+ Fifth definition term that should have green background
+ Fifth definition that should have green background
+ Sixth definition term that should have green background
+ Sixth definition that should have green background
+
+
diff --git a/HTMLKitTests/css-tests/76.html b/HTMLKitTests/css-tests/76.html
new file mode 100644
index 0000000..5791a6e
--- /dev/null
+++ b/HTMLKitTests/css-tests/76.html
@@ -0,0 +1,30 @@
+
+
+
This paragraph should be unstyled.
+
This address should be unstyled.
+
This paragraph should have green background.
+
This paragraph should have green background.
+
+ First definition term that should have green background.
+ First definition that should also have a green background.
+ Second definition term that should have green background.
+ Second definition that should have green background.
+ Third definition term.
+ Third definition.
+ Fourth definition term that should have green background.
+ Fourth definition that should have green background.
+ Fifth definition term that should have green background.
+ Fifth definition that should have green background.
+ Sixth definition term.
+ Sixth definition.
+
+
diff --git a/HTMLKitTests/css-tests/77.html b/HTMLKitTests/css-tests/77.html
new file mode 100644
index 0000000..149bf72
--- /dev/null
+++ b/HTMLKitTests/css-tests/77.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+ 1.1
+ green cell
+ green cell
+
+
+ 2.1
+ green cell
+ green cell
+
+
+ 3.1
+ green cell
+ green cell
+
+
+
+
This paragraph should be unstyled.
+
diff --git a/HTMLKitTests/css-tests/78.html b/HTMLKitTests/css-tests/78.html
new file mode 100644
index 0000000..0e7cb69
--- /dev/null
+++ b/HTMLKitTests/css-tests/78.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+ green cell
+ green cell
+ 1.3
+
+
+ green cell
+ green cell
+ 2.3
+
+
+ green cell
+ green cell
+ 3.3
+
+
+
+
This paragraph should be unstyled.
+
diff --git a/HTMLKitTests/css-tests/79.html b/HTMLKitTests/css-tests/79.html
new file mode 100644
index 0000000..36dbf48
--- /dev/null
+++ b/HTMLKitTests/css-tests/79.html
@@ -0,0 +1,13 @@
+
+
+
This div contains 3 addresses :
+
A first address with normal background
+
A second address that should have a green background
+
A third address that should have a green background
+
+
diff --git a/HTMLKitTests/css-tests/7b.html b/HTMLKitTests/css-tests/7b.html
new file mode 100644
index 0000000..6e629a6
--- /dev/null
+++ b/HTMLKitTests/css-tests/7b.html
@@ -0,0 +1,9 @@
+
+
+
This line should have a green background.
+
diff --git a/HTMLKitTests/css-tests/8.html b/HTMLKitTests/css-tests/8.html
new file mode 100644
index 0000000..741f5b6
--- /dev/null
+++ b/HTMLKitTests/css-tests/8.html
@@ -0,0 +1,21 @@
+
+
+
This paragraph should have green background because its language is "en-gb"
+
+ This address should also
+ have green background because the language of the inner SPANs id="" is not French.
+
+
diff --git a/HTMLKitTests/css-tests/80.html b/HTMLKitTests/css-tests/80.html
new file mode 100644
index 0000000..20d0cc8
--- /dev/null
+++ b/HTMLKitTests/css-tests/80.html
@@ -0,0 +1,14 @@
+
+
+
+
A first address that should have a green background
+
A second address that should have a green background
+
A third address with normal background
+ This div should have three addresses above it.
+
+
diff --git a/HTMLKitTests/css-tests/81.html b/HTMLKitTests/css-tests/81.html
new file mode 100644
index 0000000..9568093
--- /dev/null
+++ b/HTMLKitTests/css-tests/81.html
@@ -0,0 +1,16 @@
+
+
+
This paragraph should have a green background.
+
This div contains only one paragraph.
+
This paragraph should be unstyled.
+
+
diff --git a/HTMLKitTests/css-tests/82.html b/HTMLKitTests/css-tests/82.html
new file mode 100644
index 0000000..f1149ce
--- /dev/null
+++ b/HTMLKitTests/css-tests/82.html
@@ -0,0 +1,13 @@
+
+
+
+
This paragraph should have green background.
+
But this address should be unstyled.
+
This paragraph should have green background.
+
+
diff --git a/HTMLKitTests/css-tests/83.html b/HTMLKitTests/css-tests/83.html
new file mode 100644
index 0000000..74f32da
--- /dev/null
+++ b/HTMLKitTests/css-tests/83.html
@@ -0,0 +1,9 @@
+
+
+
This paragraph should have a green background
+
diff --git a/HTMLKitTests/css-tests/86.html b/HTMLKitTests/css-tests/86.html
new file mode 100644
index 0000000..659e073
--- /dev/null
+++ b/HTMLKitTests/css-tests/86.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
This text should be green.
+
+
+
+
diff --git a/HTMLKitTests/css-tests/87.html b/HTMLKitTests/css-tests/87.html
new file mode 100644
index 0000000..c3be690
--- /dev/null
+++ b/HTMLKitTests/css-tests/87.html
@@ -0,0 +1,12 @@
+
+
+
This text should be unstyled.
+
This text should be unstyled.
+
This text should be unstyled.
+
This text should be green.
+
diff --git a/HTMLKitTests/css-tests/88.html b/HTMLKitTests/css-tests/88.html
new file mode 100644
index 0000000..4b3b73e
--- /dev/null
+++ b/HTMLKitTests/css-tests/88.html
@@ -0,0 +1,14 @@
+
+
+
This text should be unstyled.
+
+
+
This text should be green.
+
+
+
diff --git a/HTMLKitTests/css-tests/89.html b/HTMLKitTests/css-tests/89.html
new file mode 100644
index 0000000..35f9d52
--- /dev/null
+++ b/HTMLKitTests/css-tests/89.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
This text should be green.
+
+
+
+
diff --git a/HTMLKitTests/css-tests/9.html b/HTMLKitTests/css-tests/9.html
new file mode 100644
index 0000000..40204f2
--- /dev/null
+++ b/HTMLKitTests/css-tests/9.html
@@ -0,0 +1,9 @@
+
+
+
This paragraph should have a green background because its title attribute begins with "foo"
+
diff --git a/HTMLKitTests/css-tests/90.html b/HTMLKitTests/css-tests/90.html
new file mode 100644
index 0000000..6344b6b
--- /dev/null
+++ b/HTMLKitTests/css-tests/90.html
@@ -0,0 +1,12 @@
+
+
+
This text should be unstyled.
+
This text should be unstyled.
+
This text should be unstyled.
+
This text should be green.
+