Merge branch 'release/0.9.2'

This commit is contained in:
iska
2016-05-18 21:17:44 +02:00
17 changed files with 423 additions and 123 deletions
+129
View File
@@ -0,0 +1,129 @@
# Change Log
## [0.9.2](https://github.com/iabudiab/HTMLKit/releases/tag/0.9.2)
Released on 2016.05.18
This release passes all html5lib-tests as of 2016.05.18
### Added
- Handling for `<menu>` and `<menuitem>`
- Changelog
### Changed
- Updated adoption agency algorithm according to the latest specification, see:
- [whatwg/html@22ce3c3](https://github.com/whatwg/html/commit/22ce3c3)
- [Mozilla Bug 901319](https://bugzilla.mozilla.org/show_bug.cgi?id=901319)
- [Chrome Issue 268121](https://bugs.chromium.org/p/chromium/issues/detail?id=268121)
- [WebKit Bug 119478](https://bugs.webkit.org/show_bug.cgi?id=119478)
- `<isindex>` is completely removed from the spec now, therefore it is dropped from the implementation
- `Tokenizer` and `Tree-Construction` tests are now generated dynamically
- Test failures are collected by a `XCTestObservation` for better reporting
- `<isindex>` is completely removed from the spec now, therefore it is dropped from the implementation
- `Tokenizer` and `Tree-Construction` tests are now generated dynamically
- Test failures are collected by a `XCTestObservation` for better reporting
### Fixed
- Parser now checks the qualified name instead of the local name when handling elements in the `MathML` and `SVG` namespaces
## [0.9.1](https://github.com/iabudiab/HTMLKit/releases/tag/0.9.1)
Released on 2016.01.29
### Added
- Travis-CI integration.
- CocoaPods spec.
### Changed
- Warnings are treated as errors.
### Fixed
- Warnings related to format specifier and loss of precision due to NS(U)-integer usage.
- Replaced `@returns` with `@return` throughout the documentation to play nicely with Jazzy.
- Some README examples used Swift syntax.
## [0.9.0](https://github.com/iabudiab/HTMLKit/releases/tag/0.9.0)
Released on 2015.12.23
This is the first public release of `HTMLKit`.
### Added
- `iOS` & `OSX` Frameworks.
- Source code documentation.
- CSS Selectors extension (analogous to jQuery selectors).
- `DOMTokenList` for malipulating `HTMLElements` attributes as a list, e.g. `class`.
- Handling for `<ruby>` elements in the Parser implementation.
- Updated HTML5Lib-Tests submodule (56c435f)
- Xcode Playground with Swift documentation.
### Removed
- Unused namespaces.
- Historical node types.
### Fixed
- `lt`, `gt` & `eq` CSS Selectors method declarations.
## [0.3.0](https://github.com/iabudiab/HTMLKit/releases/tag/0.3.0)
Released on 2015.11.29
### Added
- CSS3 Selectors support.
- Nullability annotations.
- `HTMLNode` properties for previous and next sibling elements.
- `HTMLNode` methods for accessing child elements (analogous to child nodes).
- `NSCharacterSet` category for HTML-related character sets.
### Fixed
- `InputStreaReader`'s reconsume-logic that is required by the CSS Parser.
## [0.2.0](https://github.com/iabudiab/HTMLKit/releases/tag/0.1.0)
Released on 2015.06.06
### Added
- `HTMLDocument` methods to access `root`, `head` & `body` elements.
- `innerHTML` implementation for the `HTMLElement`.
- `HTMLNode` methods to append, prepend, check containment and descendancy of nodes.
- `HTMLNode` methods to enumerate child nodes.
- Implementations for `NodeIterator` and `NodeFilter`
- Implementation for `TreeWalker`
- Validation for DOM manipulations.
- Tests for the DOM implementation.
### Changed
- `type` property renamed to `nodeType` in `HTMLNode`.
- `firstChildNode` and `lastChildNode` renamed to `firtChild` and `lastChild` in `HTMLNode`.
### Removed
- `baseURI` proeprty from `HTMLNode`
- `HTMLNodeTreeEnumerator` is superseded by the `HTMLNodeIterator`.
## [0.1.0](https://github.com/iabudiab/HTMLKit/releases/tag/0.1.0)
Released on 2015.04.20
### Added
- Initial release.
- Initial DOM implementation.
- Tokenizer and Parser pass all [HTML5Lib](https://github.com/html5lib/html5lib-tests) tokenizer and tree construction tests except for `<ruby>` elements.
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "HTMLKit"
s.version = "0.9.1"
s.version = "0.9.2"
s.summary = "HTMLKit, an Objective-C framework for your everyday HTML needs."
s.license = "MIT"
s.homepage = "https://github.com/iabudiab/HTMLKit"
+16
View File
@@ -40,6 +40,10 @@
625A14CF19C7829400AD0C32 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 625A14CD19C7829400AD0C32 /* InfoPlist.strings */; };
625D0F031C2717DE00D7BEB0 /* HTMLNode+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 625D0F011C2717DE00D7BEB0 /* HTMLNode+Private.h */; };
625D0F041C2717DE00D7BEB0 /* HTMLNode+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 625D0F011C2717DE00D7BEB0 /* HTMLNode+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
625EE4571CBAA41D00F2CC8E /* HTMLKitTestObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 625EE4561CBAA41D00F2CC8E /* HTMLKitTestObserver.m */; };
625EE4581CBAA41D00F2CC8E /* HTMLKitTestObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 625EE4561CBAA41D00F2CC8E /* HTMLKitTestObserver.m */; };
625EE45B1CBB171300F2CC8E /* HTMLKitTestUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */; };
625EE45C1CBB171300F2CC8E /* HTMLKitTestUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */; };
628AF6301BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 628AF62E1BC99A6C00496128 /* CSSNthExpressionsParserTests.m */; };
62D8345A19FB1AC4009205A9 /* HTML5LibTokenizerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 62D8345819FB1AC4009205A9 /* HTML5LibTokenizerTest.m */; };
62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62EC7AE51AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m */; };
@@ -393,6 +397,10 @@
625A150619C78ABA00AD0C32 /* HTMLInputStreamReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLInputStreamReader.h; sourceTree = "<group>"; };
625A150719C78ABA00AD0C32 /* HTMLInputStreamReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLInputStreamReader.m; sourceTree = "<group>"; };
625D0F011C2717DE00D7BEB0 /* HTMLNode+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HTMLNode+Private.h"; sourceTree = "<group>"; };
625EE4551CBAA41D00F2CC8E /* HTMLKitTestObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitTestObserver.h; sourceTree = "<group>"; };
625EE4561CBAA41D00F2CC8E /* HTMLKitTestObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTestObserver.m; sourceTree = "<group>"; };
625EE4591CBB171300F2CC8E /* HTMLKitTestUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLKitTestUtil.h; sourceTree = "<group>"; };
625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLKitTestUtil.m; sourceTree = "<group>"; };
626652F81C03D30F00C3F121 /* HTMLKitErrorDomain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLKitErrorDomain.h; sourceTree = "<group>"; };
6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLParserInsertionModes.h; sourceTree = "<group>"; };
6279F87219E1808D00F12EE5 /* HTMLElement.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.objc; fileEncoding = 4; path = HTMLElement.h; sourceTree = "<group>"; };
@@ -653,6 +661,10 @@
624E1A2D1B1D1C8A00E66AAC /* Structures */,
62FF516C1C0A430A009BFDFE /* Selectors */,
625A14CB19C7829400AD0C32 /* Supporting Files */,
625EE4551CBAA41D00F2CC8E /* HTMLKitTestObserver.h */,
625EE4561CBAA41D00F2CC8E /* HTMLKitTestObserver.m */,
625EE4591CBB171300F2CC8E /* HTMLKitTestUtil.h */,
625EE45A1CBB171300F2CC8E /* HTMLKitTestUtil.m */,
);
name = Tests;
path = HTMLKitTests;
@@ -1153,6 +1165,7 @@
6239755B1AC362CA007E26F1 /* HTMLKitTreeConstructionTests.m in Sources */,
62F658711BD83C8E0045F137 /* CSSNThExpressionSelectorTests.m in Sources */,
623CAF9E1AD88BEA00E34C32 /* HTMLKitParserPerformance.m in Sources */,
625EE4571CBAA41D00F2CC8E /* HTMLKitTestObserver.m in Sources */,
6247171D1B2240B800C11912 /* HTMLTreeWalkerTests.m in Sources */,
62EC7AE71AEEAC6F0015D3BE /* HTMLKitMutationAlgorithmsTests.m in Sources */,
624AB31B1B050A4D00F3830D /* CSSAttributeSelectorTests.m in Sources */,
@@ -1168,6 +1181,7 @@
624FC37B1AE591D80015DDF9 /* HTMLKitNodesTests.m in Sources */,
621FBE5B1BDAD68700BC9555 /* CSSSelectorParserTests.m in Sources */,
621FBE5E1BDAD90200BC9555 /* CSSCombinatorSelectorTests.m in Sources */,
625EE45B1CBB171300F2CC8E /* HTMLKitTestUtil.m in Sources */,
628AF6301BC99A6C00496128 /* CSSNthExpressionsParserTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1233,6 +1247,7 @@
62ECBFCB1C0B6E2E00AF847B /* HTMLKitTokenizerTests.m in Sources */,
62ECBFCC1C0B6E2E00AF847B /* HTMLKitTokenizerPerformance.m in Sources */,
62ECBFCD1C0B6E2E00AF847B /* HTML5LibTreeConstructionTest.m in Sources */,
625EE4581CBAA41D00F2CC8E /* HTMLKitTestObserver.m in Sources */,
62ECBFCE1C0B6E2E00AF847B /* HTMLKitTreeConstructionTests.m in Sources */,
62ECBFCF1C0B6E2E00AF847B /* HTMLKitParserPerformance.m in Sources */,
62ECBFD01C0B6E2E00AF847B /* HTMLKitNodeIteratorTests.m in Sources */,
@@ -1248,6 +1263,7 @@
62ECBFD91C0B6E2E00AF847B /* CSSTypeSelectorTests.m in Sources */,
62ECBFDA1C0B6E2E00AF847B /* CSSAttributeSelectorTests.m in Sources */,
62ECBFDB1C0B6E2E00AF847B /* CSSNThExpressionSelectorTests.m in Sources */,
625EE45C1CBB171300F2CC8E /* HTMLKitTestUtil.m in Sources */,
62ECBFDC1C0B6E2E00AF847B /* CSSCombinatorSelectorTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
+2 -2
View File
@@ -39,8 +39,8 @@ NS_INLINE BOOL IsSpecialElement(HTMLElement *element)
@"dir", @"div", @"dl", @"dt", @"embed", @"fieldset", @"figcaption",
@"figure", @"footer", @"form", @"frame", @"frameset", @"h1", @"h2", @"h3",
@"h4", @"h5", @"h6", @"head", @"header", @"hgroup", @"hr", @"html", @"iframe",
@"img", @"input", @"isindex", @"li", @"link", @"listing", @"main", @"marquee",
@"menu", @"menuitem", @"meta", @"nav", @"noembed", @"noframes", @"noscript",
@"img", @"input", @"li", @"link", @"listing", @"main", @"marquee",
@"menu", @"meta", @"nav", @"noembed", @"noframes", @"noscript",
@"object", @"ol", @"p", @"param", @"plaintext", @"pre", @"script", @"section",
@"select", @"source", @"style", @"summary", @"table", @"tbody", @"td",
@"template", @"textarea", @"tfoot", @"th", @"thead", @"title", @"tr",
+1 -1
View File
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.9.1</string>
<string>0.9.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
+39 -59
View File
@@ -519,7 +519,7 @@
- (void)generateImpliedEndTagsExceptForElement:(NSString *)tagName
{
while ([self.currentNode.tagName isEqualToAny:@"dd", @"dt", @"li", @"option", @"optgroup", @"p", @"rb", @"rp", @"rt", @"rtc", nil] &&
while ([self.currentNode.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"option", @"optgroup", @"p", @"rb", @"rp", @"rt", @"rtc", nil] &&
![self.currentNode.tagName isEqualToString:tagName]) {
[_stackOfOpenElements popCurrentNode];
}
@@ -588,20 +588,29 @@
HTMLElement *lastNode = furthestBlock;
NSUInteger index = [_stackOfOpenElements indexOfElement:node];
for (int innerLoopCounter = 0; innerLoopCounter < 3; innerLoopCounter ++) {
index--;
int innerLoopCounter = 0;
while (YES) {
innerLoopCounter += 1;
index -= 1;
node = _stackOfOpenElements[index];
if ([node isEqual:formattingElement]) {
break;
}
if (innerLoopCounter > 3 && [_listOfActiveFormattingElements containsElement:node]) {
[_listOfActiveFormattingElements removeElement:node];
continue;
}
if (![_listOfActiveFormattingElements containsElement:node]) {
[_stackOfOpenElements removeElement:node];
continue;
}
if ([node isEqual:formattingElement]) {
break;
}
HTMLElement *newElement = [node copy];
[_listOfActiveFormattingElements replaceElementAtIndex:[_listOfActiveFormattingElements indexOfElement:node]
withElement:newElement];
@@ -1158,8 +1167,8 @@
[self HTMLInsertionModeInTemplate:token];
} else {
for (HTMLElement *node in _stackOfOpenElements) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"optgroup", @"option", @"p", @"rb", @"rp",
@"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"optgroup", @"option", @"p", @"rb",
@"rp", @"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
[self emitParseError:@"EOF reached with unclosed element <%@> in <body>", node.tagName];
break;
}
@@ -1222,12 +1231,20 @@
[self switchInsertionMode:HTMLInsertionModeInFrameset];
} else if ([tagName isEqualToAny:@"address", @"article", @"aside", @"blockquote", @"center",
@"details", @"dialog", @"dir", @"div", @"dl", @"fieldset", @"figcaption", @"figure",
@"footer", @"header", @"hgroup", @"main", @"menu", @"nav", @"ol", @"p", @"section",
@"footer", @"header", @"hgroup", @"main", @"nav", @"ol", @"p", @"section",
@"summary", @"ul", nil]) {
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
}
[self insertElementForToken:token];
} else if ([tagName isEqualToString:@"menu"]) {
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
}
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
[_stackOfOpenElements popCurrentNode];
}
[self insertElementForToken:token];
} else if ([tagName isEqualToAny:@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", nil]) {
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
@@ -1364,13 +1381,16 @@
if (type == nil || ![type isEqualToStringIgnoringCase:@"hidden"]) {
_framesetOkFlag = NO;
}
} else if ([tagName isEqualToAny:@"menuitem", @"param", @"source", @"track", nil]) {
} else if ([tagName isEqualToAny:@"param", @"source", @"track", nil]) {
[self insertElementForToken:token];
[_stackOfOpenElements popCurrentNode];
} else if ([tagName isEqualToString:@"hr"]) {
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
}
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
[_stackOfOpenElements popCurrentNode];
}
[self insertElementForToken:token];
[_stackOfOpenElements popCurrentNode];
_framesetOkFlag = NO;
@@ -1378,52 +1398,6 @@
[self emitParseError:@"Image Start Tag Token with tagname <image> should be <img>. Don't ask."];
token.tagName = @"img";
[self reprocessToken:token];
} else if ([tagName isEqualToString:@"isindex"]) {
[self emitParseError:@"Unexpected start tag <isindex> in <body>"];
if (_formElementPointer != nil && ![_stackOfOpenElements containsElementWithTagName:@"template"]) {
return;
}
_framesetOkFlag = NO;
if ([_stackOfOpenElements hasElementInButtonScopeWithTagName:@"p"]) {
[self closePElement];
}
HTMLStartTagToken *formToken = [[HTMLStartTagToken alloc] initWithTagName:@"form"];
HTMLElement *form = [self insertElementForToken:formToken];
if (![_stackOfOpenElements containsElementWithTagName:@"template"]) {
_formElementPointer = form;
}
NSString *action = token.attributes[@"action"];
if (action != nil) {
form.attributes[@"action"] = action;
}
HTMLStartTagToken *hrToken = [[HTMLStartTagToken alloc] initWithTagName:@"hr"];
[self insertElementForToken:hrToken];
[_stackOfOpenElements popCurrentNode];
[self reconstructActiveFormattingElements];
HTMLStartTagToken *labelToken = [[HTMLStartTagToken alloc] initWithTagName:@"label"];
[self insertElementForToken:labelToken];
NSString *prompt = token.attributes[@"prompt"] ?: @"This is a searchable index. Enter search keywords: ";
[self insertCharacters:prompt];
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithDictionary:token.attributes];
attributes[@"name"] = @"isindex";
[attributes removeObjectForKey:@"action"];
[attributes removeObjectForKey:@"prompt"];
HTMLStartTagToken *inputToken = [[HTMLStartTagToken alloc] initWithTagName:@"input" attributes:attributes];
[self insertElementForToken:inputToken];
[_stackOfOpenElements popCurrentNode];
[_stackOfOpenElements popCurrentNode];
[self insertElementForToken:hrToken];
[_stackOfOpenElements popCurrentNode];
[_stackOfOpenElements popCurrentNode];
_formElementPointer = nil;
} else if ([tagName isEqualToString:@"textarea"]) {
[self insertElementForToken:token];
_ignoreNextLineFeedCharacterToken = YES;
@@ -1462,6 +1436,12 @@
}
[self reconstructActiveFormattingElements];
[self insertElementForToken:token];
} else if ([tagName isEqualToString:@"menuitem"]) {
if ([self.currentNode.tagName isEqualToString:@"menuitem"]) {
[_stackOfOpenElements popCurrentNode];
}
[self reconstructActiveFormattingElements];
[self insertElementForToken:token];
} else if ([tagName isEqualToAny:@"rb", @"rtc", nil]) {
if ([_stackOfOpenElements hasElementInScopeWithTagName:@"ruby"]) {
[self generateImpliedEndTagsExceptForElement:nil];
@@ -1516,8 +1496,8 @@
[self emitParseError:@"Unexpected end tag </body> without body element in scope in <body>"];
}
for (HTMLElement *node in _stackOfOpenElements) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"optgroup", @"option", @"p", @"rb", @"rp", @"rt",
@"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
if ([node.tagName isEqualToAny:@"dd", @"dt", @"li", @"menuitem", @"optgroup", @"option", @"p", @"rb", @"rp",
@"rt", @"rtc", @"tbody", @"td", @"tfoot", @"th", @"thead", @"tr", @"body", @"html", nil]) {
[self emitParseError:@"Misnested end tag </%@> with open element <%@> in <body>", tagName, node.tagName];
break;
}
+14 -3
View File
@@ -129,7 +129,11 @@
- (void)popElementsUntilElementPoppedWithTagName:(NSString *)tagName
{
while (self.currentNode && ![self.currentNode.tagName isEqualToString:tagName]) {
while (self.currentNode) {
if (self.currentNode.htmlNamespace == HTMLNamespaceHTML &&
[self.currentNode.tagName isEqualToString:tagName]) {
break;
}
[_stack removeLastObject];
}
[_stack removeLastObject];
@@ -137,7 +141,11 @@
- (void)popElementsUntilAnElementPoppedWithAnyOfTagNames:(NSArray *)tagNames
{
while (self.currentNode && ![tagNames containsObject:self.currentNode.tagName]) {
while (self.currentNode) {
if (self.currentNode.htmlNamespace == HTMLNamespaceHTML &&
[tagNames containsObject:self.currentNode.tagName]) {
break;
}
[_stack removeLastObject];
}
[_stack removeLastObject];
@@ -257,7 +265,10 @@
{
for (HTMLElement *node in _stack.reverseObjectEnumerator) {
if ([tagNames containsObject:node.tagName]) {
return node;
NSNumber *namespace = elementTypes[node.tagName] ?: @(HTMLNamespaceHTML);
if ([namespace isEqual:@(node.htmlNamespace)]) {
return node;
}
}
if ([elementTypes[node.tagName] isEqual:@(node.htmlNamespace)]) {
return nil;
+1 -1
View File
@@ -10,7 +10,7 @@
@interface HTML5LibTokenizerTest : NSObject
@property (nonatomic, copy) NSString *testName;
@property (nonatomic, copy) NSString *testFile;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *input;
@property (nonatomic, strong) NSArray *output;
+2 -2
View File
@@ -39,7 +39,7 @@ static NSString * const TOKENIZER = @"tokenizer";
+ (NSArray *)loadTestsWithFileAtPath:(NSString *)filePath
{
NSString *testName = filePath.lastPathComponent.stringByDeletingLastPathComponent;
NSString *testFile = filePath.lastPathComponent;
NSString *json = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
@@ -52,7 +52,7 @@ static NSString * const TOKENIZER = @"tokenizer";
for (NSDictionary *test in jsonTests) {
HTML5LibTokenizerTest *html5libTest = [[HTML5LibTokenizerTest alloc] initWithTestDictionary:test];
html5libTest.testName = testName;
html5libTest.testFile = testFile;
[tests addObject:html5libTest];
}
return tests;
+24
View File
@@ -0,0 +1,24 @@
//
// HTMLKitTestObserver.h
// HTMLKit
//
// Created by Iska on 10/04/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface HTMLKitTestReport : NSObject
@property (assign, readonly) NSUInteger totalCount;
@property (assign, readonly) NSUInteger failureCount;
@property (copy, readonly) NSString *failureReport;
@end
@interface HTMLKitTestObserver<TestCase: XCTestCase *> : NSObject <XCTestObservation>
- (instancetype)initWithName:(NSString *)name;
- (void)addCaseForHTML5LibTestWithInput:(NSString *)input;
- (HTMLKitTestReport *)generateReport;
@end
+86
View File
@@ -0,0 +1,86 @@
//
// HTMLKitTestObserver.m
// HTMLKit
//
// Created by Iska on 10/04/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLKitTestObserver.h"
#pragma mark - HTMLKitTestReport
@interface HTMLKitTestReport ()
@property (assign) NSUInteger totalCount;
@property (assign) NSUInteger failureCount;
@property (copy) NSString *failureReport;
@end
@implementation HTMLKitTestReport
@synthesize totalCount, failureCount, failureReport;
@end
#pragma mark - HTMLKitTestObserver
@interface HTMLKitTestObserver ()
{
NSString *_name;
NSMutableArray *_cases;
NSMutableDictionary *_currentCase;
}
@end
@implementation HTMLKitTestObserver
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self) {
_name = [name copy];
_cases = [NSMutableArray new];
}
return self;
}
- (void)addCaseForHTML5LibTestWithInput:(NSString *)input
{
_currentCase = [NSMutableDictionary new];
_currentCase[@"input"] = input;
_currentCase[@"status"] = @"Passed";
[_cases addObject:_currentCase];
}
- (void)testCase:(XCTestCase *)testCase didFailWithDescription:(NSString *)description inFile:(NSString *)filePath atLine:(NSUInteger)lineNumber
{
_currentCase[@"status"] = @"Failed";
}
- (HTMLKitTestReport *)generateReport
{
NSMutableString *reportDescription = [NSMutableString string];
NSIndexSet *failedIndexes = [_cases indexesOfObjectsPassingTest:^BOOL(NSDictionary *testCase, NSUInteger idx, BOOL * _Nonnull stop) {
return [testCase[@"status"] isEqualToString:@"Failed"];
}];
NSArray *failedTests = [_cases objectsAtIndexes:failedIndexes];
NSUInteger totalCount = _cases.count;
NSUInteger failureCount = failedTests.count;
[reportDescription appendFormat:@"HTML5Lib test %@ failed [%lu] out of [%lu] total tests\n", _name, failureCount, _cases.count];
for (NSDictionary *testCase in failedTests) {
[reportDescription appendFormat:@"Failed test for input: %@\n", testCase[@"input"]];
}
HTMLKitTestReport *report = [HTMLKitTestReport new];
report.totalCount = totalCount;
report.failureCount = failureCount;
report.failureReport = reportDescription;
return report;
}
@end
+15
View File
@@ -0,0 +1,15 @@
//
// HTMLKitTestUtil.h
// HTMLKit
//
// Created by Iska on 11/04/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface HTMLKitTestUtil : NSObject
+ (NSInvocation *)addTestToClass:(Class)cls withName:(NSString *)name block:(id)block;
@end
+29
View File
@@ -0,0 +1,29 @@
//
// HTMLKitTestUtil.m
// HTMLKit
//
// Created by Iska on 11/04/16.
// Copyright © 2016 BrainCookie. All rights reserved.
//
#import "HTMLKitTestUtil.h"
#import <objc/runtime.h>
@implementation HTMLKitTestUtil
+ (NSInvocation *)addTestToClass:(Class)cls withName:(NSString *)name block:(id)block
{
IMP implementation = imp_implementationWithBlock(block);
const char *types = [[NSString stringWithFormat:@"%s%s%s", @encode(id), @encode(id), @encode(SEL)] UTF8String];
SEL selector = NSSelectorFromString(name);
class_addMethod(cls, selector, implementation, types);
NSMethodSignature *signature = [cls instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = selector;
return invocation;
}
@end
+15 -27
View File
@@ -7,8 +7,10 @@
//
#import <XCTest/XCTest.h>
#import "HTML5LibTokenizerTest.h"
#import "HTMLKitTestUtil.h"
#import "HTML5LibTokenizerTest.h"
#import "HTMLTokenizer.h"
#import "HTMLTokenizerStates.h"
#import "HTMLTokens.h"
@@ -30,7 +32,6 @@
#pragma mark - HTML5Lib Test Suite
@interface HTMLKitTokenizerTests : XCTestCase
@property (nonatomic, strong) NSString *testName;
@property (nonatomic, strong) NSArray *testsList;
@end
@@ -50,44 +51,31 @@
+ (void)addTestCaseForTestFile:(NSString *)testFile withTests:(NSArray *)tests toTestSuite:(XCTestSuite *)suite
{
NSArray *allInvocations = [self testInvocations];
for (NSInvocation *invocation in allInvocations) {
XCTestCase *testCase = [[self alloc] initWithInvocation:invocation
testName:testFile
tests:tests];
[suite addTest:testCase];
}
NSString *testName = [testFile.stringByDeletingPathExtension stringByReplacingOccurrencesOfString:@"-" withString:@"_"];
testName = [NSString stringWithFormat:@"testTokenizer__%@", testName];
NSInvocation *invocation = [HTMLKitTestUtil addTestToClass:self withName:testName block:^ (HTMLKitTokenizerTests *instance){
[instance runTests];
}];
XCTestCase *testCase = [[self alloc] initWithInvocation:invocation tests:tests];
[suite addTest:testCase];
}
#pragma mark - Instance
- (instancetype)initWithInvocation:(NSInvocation *)invocation
testName:(NSString *)testName
tests:(NSArray *)tests
- (instancetype)initWithInvocation:(NSInvocation *)invocation tests:(NSArray *)tests
{
self = [super initWithInvocation:invocation];
if (self) {
_testName = testName;
_testsList = tests;
}
return self;
}
- (NSString *)name
{
NSInvocation *invocation = [self invocation];
NSString *title = self.testName.stringByDeletingPathExtension;
return [NSString stringWithFormat:@"-[%@ %@_%@]", self.class, NSStringFromSelector(invocation.selector), title];
}
- (NSString *)description
{
return self.name;
}
#pragma mark - Tests
- (void)testTokenizer
- (void)runTests
{
for (HTML5LibTokenizerTest *test in self.testsList) {
@@ -101,7 +89,7 @@
NSArray *tokens = tokenizer.allObjects;
NSString *message = [NSString stringWithFormat:@"HTML5Lib test in file: \'%@\' Title: '%@'\nInput: '%@'\nExpected:\n%@\nActual:\n%@\n",
self.testName,
test.testFile,
test.title,
test.input,
expectedTokens,
+35 -22
View File
@@ -8,6 +8,9 @@
#import <XCTest/XCTest.h>
#import "HTMLKitTestUtil.h"
#import "HTMLKitTestObserver.h"
#import "HTML5LibTreeConstructionTest.h"
#import "HTMLDOM.h"
#import "HTMLParser.h"
@@ -26,7 +29,9 @@
#pragma mark - HTML5Lib Test Suite
@interface HTMLKitTreeConstructionTests : XCTestCase
@property (nonatomic, strong) NSString *testName;
{
HTMLKitTestObserver<HTMLKitTreeConstructionTests *> *_observer;
}
@property (nonatomic, strong) NSArray *testsList;
@end
@@ -45,50 +50,58 @@
+ (void)addTestCaseForTestFile:(NSString *)testFile withTests:(NSArray *)tests toTestSuite:(XCTestSuite *)suite
{
NSArray *allInvocations = [self testInvocations];
for (NSInvocation *invocation in allInvocations) {
XCTestCase *testCase = [[self alloc] initWithInvocation:invocation
testName:testFile
tests:tests];
[suite addTest:testCase];
}
NSString *testName = [testFile.stringByDeletingPathExtension stringByReplacingOccurrencesOfString:@"-" withString:@"_"];
testName = [NSString stringWithFormat:@"testPareser__%@", testName];
NSInvocation *invocation = [HTMLKitTestUtil addTestToClass:self withName:testName block:^ (HTMLKitTreeConstructionTests *instance){
[instance runTests];
}];
XCTestCase *testCase = [[self alloc] initWithInvocation:invocation tests:tests];
[suite addTest:testCase];
}
#pragma mark - Instance
- (instancetype)initWithInvocation:(NSInvocation *)invocation
testName:(NSString *)testName
tests:(NSArray *)tests
- (instancetype)initWithInvocation:(NSInvocation *)invocation tests:(NSArray *)tests
{
self = [super initWithInvocation:invocation];
if (self) {
_testName = testName;
_testsList = tests;
}
return self;
}
- (NSString *)name
#pragma mark - Setup
- (void)setUp
{
NSInvocation *invocation = [self invocation];
NSString *title = self.testName.stringByDeletingPathExtension;
return [NSString stringWithFormat:@"-[%@ %@_%@]", self.class, NSStringFromSelector(invocation.selector), title];
_observer = [[HTMLKitTestObserver alloc] initWithName:self.name];
[[XCTestObservationCenter sharedTestObservationCenter] addTestObserver:_observer];
[super setUp];
}
- (NSString *)description
- (void)tearDown
{
return self.name;
HTMLKitTestReport *testReport = [_observer generateReport];
XCTAssertTrue(testReport.failureCount == 0, @"%@", testReport.failureReport);
[[XCTestObservationCenter sharedTestObservationCenter] removeTestObserver:_observer];
[super tearDown];
}
#pragma mark - Tests
- (void)testParser
- (void)runTests
{
for (HTML5LibTreeConstructionTest *test in self.testsList) {
NSString *testInput = test.data;
[_observer addCaseForHTML5LibTestWithInput:testInput];
HTMLParser *parser = [[HTMLParser alloc] initWithString:testInput];
HTMLElement *contextElement = test.documentFragment;
HTMLParser *parser = [[HTMLParser alloc] initWithString:test.data];
NSArray *actual = nil;
if (contextElement == nil) {
actual = [parser parseDocument].childNodes.array;
+13 -4
View File
@@ -1,10 +1,15 @@
# HTMLKit
![HTMLKit Logo](HTMLKit.png)
![HTMLKit Logo](https://raw.githubusercontent.com/iabudiab/HTMLKit/master/HTMLKit.png)
An Objective-C framework for your everyday HTML needs.
[![Build Status](https://travis-ci.org/iabudiab/HTMLKit.svg?branch=develop)](https://travis-ci.org/iabudiab/HTMLKit) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![License MIT](https://img.shields.io/badge/license-MIT-4481C7.svg)](https://opensource.org/licenses/MIT)
[![Build Status](https://img.shields.io/travis/iabudiab/HTMLKit/develop.svg?style=flat)](https://travis-ci.org/iabudiab/HTMLKit)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/HTMLKit.svg?style=flat)](https://cocoapods.org/pods/HTMLKit)
[![CocoaDocs](https://img.shields.io/cocoapods/metrics/doc-percent/HTMLKit.svg?style=flat)](http://cocoadocs.org/docsets/HTMLKit)
[![Platform](https://img.shields.io/cocoapods/p/HTMLKit.svg?style=flat)](http://cocoadocs.org/docsets/HTMLKit)
[![License MIT](https://img.shields.io/badge/license-MIT-4481C7.svg?style=flat)](https://opensource.org/licenses/MIT)
# Quick Overview
@@ -18,7 +23,7 @@ DOM mutations are validated as described in the [WHATWG DOM Standard](https://do
## Tests
HTMLKit passes all of the [HTML5Lib](https://github.com/html5lib/html5lib-tests) Tokenizer and Tree Construction tests except for the Blink changes introduced on the 16.09.2015. The `html5lib-tests` is configured as a git-submodule. If you plan to run the tests, do not forget to pull it too.
HTMLKit passes all of the [HTML5Lib](https://github.com/html5lib/html5lib-tests) Tokenizer and Tree Construction tests. The `html5lib-tests` is configured as a git-submodule. If you plan to run the tests, do not forget to pull it too.
The CSS3 Selector implementation is tested with an adapted version of the [CSS3 Selectors Test Suite](http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/html/full/flat/index.html), ignoring the tests that require user interaction, session history, and scripting.
@@ -214,6 +219,10 @@ CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];
```
# Change Log
See the [CHANGELOG.md](CHANGELOG.md) for more info.
# License
HTMLKit is available under the MIT license. See the LICENSE file for more info.
HTMLKit is available under the MIT license. See the [LICENSE](LICENSE) file for more info.