From d65463b4d66d149c8f2e6cd7f52aca3ade7af3dd Mon Sep 17 00:00:00 2001 From: iska Date: Mon, 2 Mar 2015 23:56:31 +0100 Subject: [PATCH] Add implementation for Active Formatting Element Reconstruction https://html.spec.whatwg.org/multipage/syntax.html#reconstruct-the-active-formatting-elements --- HTMLKit.xcodeproj/project.pbxproj | 10 ++++++ HTMLKit/HTMLElement.h | 2 ++ HTMLKit/HTMLMarker.h | 15 +++++++++ HTMLKit/HTMLMarker.m | 23 +++++++++++++ HTMLKit/HTMLParser.m | 56 +++++++++++++++++++++++++++++++ HTMLKit/HTMLTagToken.h | 1 + HTMLKit/HTMLTagToken.m | 23 +++++++++++-- 7 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 HTMLKit/HTMLMarker.h create mode 100644 HTMLKit/HTMLMarker.m diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index 13906ec..12727ea 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -13,6 +13,9 @@ 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 */; }; @@ -121,6 +124,8 @@ 6234584D1A9D2FA4009BD491 /* HTMLNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLNode.m; sourceTree = ""; }; 6235CE991AA509430026937B /* NSString+HTMLKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+HTMLKit.h"; sourceTree = ""; }; 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+HTMLKit.m"; sourceTree = ""; }; + 6235CE9E1AA5170A0026937B /* HTMLMarker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLMarker.h; sourceTree = ""; }; + 6235CE9F1AA5170A0026937B /* HTMLMarker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLMarker.m; sourceTree = ""; }; 62362A3A1A9FA70400301989 /* HTMLText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLText.h; sourceTree = ""; }; 62362A3B1A9FA70400301989 /* HTMLText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLText.m; sourceTree = ""; }; 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLNodes.h; sourceTree = ""; }; @@ -298,6 +303,8 @@ 628B7CE61A080E1000602C87 /* HTMLNamespaces.h */, 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */, 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */, + 6235CE9E1AA5170A0026937B /* HTMLMarker.h */, + 6235CE9F1AA5170A0026937B /* HTMLMarker.m */, 6235CE991AA509430026937B /* NSString+HTMLKit.h */, 6235CE9A1AA509430026937B /* NSString+HTMLKit.m */, 623719441AA1472B002E03C8 /* Nodes */, @@ -382,6 +389,7 @@ 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 */, 6279F87419E1808D00F12EE5 /* HTMLElement.h in Headers */, 62AE593F19F9907C0043F069 /* HTMLTagToken.h in Headers */, @@ -549,6 +557,7 @@ 6235CE9D1AA509430026937B /* NSString+HTMLKit.m in Sources */, 6238579D1A9E8934003A45D9 /* HTMLComment.m in Sources */, 624493AE19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, + 6235CEA21AA5170A0026937B /* HTMLMarker.m in Sources */, 62AE594119F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594619F992F30043F069 /* HTMLCommentToken.m in Sources */, 62AE593C19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, @@ -584,6 +593,7 @@ 6235CE9C1AA509430026937B /* NSString+HTMLKit.m in Sources */, 6238579C1A9E8934003A45D9 /* HTMLComment.m in Sources */, 624493AD19CD0CBE00BCDDF4 /* HTMLToken.m in Sources */, + 6235CEA11AA5170A0026937B /* HTMLMarker.m in Sources */, 62AE594019F9907C0043F069 /* HTMLTagToken.m in Sources */, 62AE594519F992F30043F069 /* HTMLCommentToken.m in Sources */, 62AE593B19F97E1C0043F069 /* HTMLDOCTYPEToken.m in Sources */, diff --git a/HTMLKit/HTMLElement.h b/HTMLKit/HTMLElement.h index 24ba656..ea0f15e 100644 --- a/HTMLKit/HTMLElement.h +++ b/HTMLKit/HTMLElement.h @@ -16,6 +16,8 @@ @property (nonatomic, copy, readonly) NSString *tagName; +@property (nonatomic, strong) NSMutableDictionary *attributes; + @property (nonatomic, copy) NSString *id; @property (nonatomic, copy) NSString *className; diff --git a/HTMLKit/HTMLMarker.h b/HTMLKit/HTMLMarker.h new file mode 100644 index 0000000..1aa2ef8 --- /dev/null +++ b/HTMLKit/HTMLMarker.h @@ -0,0 +1,15 @@ +// +// HTMLMarker.h +// HTMLKit +// +// Created by Iska on 02/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import + +@interface HTMLMarker : NSObject + ++ (instancetype)marker; + +@end diff --git a/HTMLKit/HTMLMarker.m b/HTMLKit/HTMLMarker.m new file mode 100644 index 0000000..c76a655 --- /dev/null +++ b/HTMLKit/HTMLMarker.m @@ -0,0 +1,23 @@ +// +// HTMLMarker.m +// HTMLKit +// +// Created by Iska on 02/03/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +#import "HTMLMarker.h" + +@implementation HTMLMarker + ++ (instancetype)marker +{ + static dispatch_once_t onceToken; + static HTMLMarker *singleton = nil; + dispatch_once(&onceToken, ^{ + singleton = [[self alloc] init]; + }); + return singleton; +} + +@end diff --git a/HTMLKit/HTMLParser.m b/HTMLKit/HTMLParser.m index 15bf02f..3f70bae 100644 --- a/HTMLKit/HTMLParser.m +++ b/HTMLKit/HTMLParser.m @@ -12,6 +12,7 @@ #import "HTMLParserInsertionModes.h" #import "HTMLNodes.h" #import "HTMLElementTypes.h" +#import "HTMLMarker.h" #import "NSString+HTMLKit.h" @interface HTMLParser () @@ -25,6 +26,7 @@ HTMLInsertionMode _originalInsertionMode; NSMutableArray *_stackOfOpenElements; + NSMutableArray *_listOfActiveFormattingElements; HTMLDocument *_document; @@ -56,6 +58,7 @@ [self setupStateMachine]; _stackOfOpenElements = [NSMutableArray new]; + _listOfActiveFormattingElements = [NSMutableArray new]; _tokenizer = [[HTMLTokenizer alloc] initWithString:string]; _scriptingFlag = NO; @@ -424,6 +427,59 @@ [self switchInsertionMode:HTMLInsertionModeText]; } +- (void)reconstructActiveFormattingElements +{ + if (_listOfActiveFormattingElements.count == 0) { + return; + } + + id last = _listOfActiveFormattingElements.lastObject; + if (last == [HTMLMarker marker] || [_stackOfOpenElements containsObject:last]) { + return; + } + + __block NSUInteger index = _listOfActiveFormattingElements.count - 1; + __block id entry = _listOfActiveFormattingElements[index]; + + // Reconstruct the active formatting elements + // https://html.spec.whatwg.org/multipage/syntax.html#reconstruct-the-active-formatting-elements + // No cycles ~> blocks instead of gotos + + dispatch_block_t advance = ^{ + entry = _listOfActiveFormattingElements[index++]; + }; + + dispatch_block_t create = ^{ + HTMLElement *entry = _listOfActiveFormattingElements[index]; + HTMLStartTagToken *token = [[HTMLStartTagToken alloc] initWithTagName:entry.tagName + attributes:entry.attributes]; + HTMLElement *element = [self insertElementForToken:token]; + [_listOfActiveFormattingElements replaceObjectAtIndex:index withObject:element]; + if (index++ != _listOfActiveFormattingElements.count) { + advance(); + } + }; + + dispatch_block_t rewind = ^{ + if (index == 0) { + create(); + } + entry = _listOfActiveFormattingElements[index--]; + if (entry != [HTMLMarker marker] && ![_stackOfOpenElements containsObject:entry]) { + rewind(); + } + }; + + rewind(); +} + +- (void)clearListOfActiveFormattingElementsUptoLastMarker +{ + while (_listOfActiveFormattingElements.lastObject != [HTMLMarker marker]) { + [_listOfActiveFormattingElements removeLastObject]; + } +} + #pragma mark - Insertion Modes - (void)HTMLInsertionModeInitial:(HTMLToken *)token diff --git a/HTMLKit/HTMLTagToken.h b/HTMLKit/HTMLTagToken.h index ee04aa2..7b9987f 100644 --- a/HTMLKit/HTMLTagToken.h +++ b/HTMLKit/HTMLTagToken.h @@ -17,6 +17,7 @@ @property (nonatomic, assign, getter = isSelfClosing) BOOL selfClosing; - (instancetype)initWithTagName:(NSString *)tagName; +- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSMutableDictionary *)attributes; - (void)appendStringToTagName:(NSString *)string; diff --git a/HTMLKit/HTMLTagToken.m b/HTMLKit/HTMLTagToken.m index 2526991..39eee74 100644 --- a/HTMLKit/HTMLTagToken.m +++ b/HTMLKit/HTMLTagToken.m @@ -21,10 +21,19 @@ @synthesize tagName = _tagName; - (instancetype)initWithTagName:(NSString *)tagName +{ + return [self initWithTagName:tagName attributes:nil]; +} + +- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSMutableDictionary *)attributes { self = [super init]; if (self) { _tagName = [tagName mutableCopy]; + _attributes = [NSMutableDictionary new]; + if (attributes != nil) { + [_attributes addEntriesFromDictionary:attributes]; + } } return self; } @@ -44,7 +53,12 @@ - (instancetype)initWithTagName:(NSString *)tagName { - self = [super initWithTagName:tagName]; + return [self initWithTagName:tagName attributes:nil]; +} + +- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSMutableDictionary *)attributes +{ + self = [super initWithTagName:tagName attributes:attributes]; if (self) { self.type = HTMLTokenTypeStartTag; } @@ -80,7 +94,12 @@ - (instancetype)initWithTagName:(NSString *)tagName { - self = [super initWithTagName:tagName]; + return [self initWithTagName:tagName attributes:nil]; +} + +- (instancetype)initWithTagName:(NSString *)tagName attributes:(NSMutableDictionary *)attributes +{ + self = [super initWithTagName:tagName attributes:attributes]; if (self) { self.type = HTMLTokenTypeEndTag; }