diff --git a/HTMLKit.xcodeproj/project.pbxproj b/HTMLKit.xcodeproj/project.pbxproj index f7dbba3..4ce9ffa 100644 --- a/HTMLKit.xcodeproj/project.pbxproj +++ b/HTMLKit.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ 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 = ""; }; + 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLQuirksMode.h; sourceTree = ""; }; 6238578F1A9E772B003A45D9 /* HTMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDocument.h; sourceTree = ""; }; 623857901A9E772B003A45D9 /* HTMLDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTMLDocument.m; sourceTree = ""; }; 623857941A9E8606003A45D9 /* HTMLDocumentType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLDocumentType.h; sourceTree = ""; }; @@ -272,6 +273,7 @@ 6223211D1A969B9300BACED5 /* HTMLElementTypes.h */, 6279F87119E17DC700F12EE5 /* HTMLParserInsertionModes.h */, 62362A3F1A9FDE8A00301989 /* HTMLNodes.h */, + 623719431AA12EE8002E03C8 /* HTMLQuirksMode.h */, 6234584C1A9D2FA4009BD491 /* HTMLNode.h */, 6234584D1A9D2FA4009BD491 /* HTMLNode.m */, 6279F87219E1808D00F12EE5 /* HTMLElement.h */, diff --git a/HTMLKit/HTMLDocument.h b/HTMLKit/HTMLDocument.h index 4ce662c..cc4e1b3 100644 --- a/HTMLKit/HTMLDocument.h +++ b/HTMLKit/HTMLDocument.h @@ -8,13 +8,7 @@ #import "HTMLNode.h" #import "HTMLDocumentType.h" - -typedef NS_ENUM(short, HTMLQuirksMode) -{ - HTMLQuirksModeNoQuirks, - HTMLQuirksModeQuirks, - HTMLQuirksModeLimitedQuirks -}; +#import "HTMLQuirksMode.h" @interface HTMLDocument : HTMLNode diff --git a/HTMLKit/HTMLDocumentType.h b/HTMLKit/HTMLDocumentType.h index f02d42e..d872730 100644 --- a/HTMLKit/HTMLDocumentType.h +++ b/HTMLKit/HTMLDocumentType.h @@ -7,6 +7,7 @@ // #import "HTMLNode.h" +#import "HTMLQuirksMode.h" @interface HTMLDocumentType : HTMLNode @@ -18,4 +19,7 @@ publicIdentifier:(NSString *)publicIdentifier systemIdentifier:(NSString *)systemIdentifier; +- (BOOL)isValid; +- (HTMLQuirksMode)quirksMode; + @end diff --git a/HTMLKit/HTMLDocumentType.m b/HTMLKit/HTMLDocumentType.m index 748f550..3a07c98 100644 --- a/HTMLKit/HTMLDocumentType.m +++ b/HTMLKit/HTMLDocumentType.m @@ -8,6 +8,10 @@ #import "HTMLDocumentType.h" +NS_INLINE BOOL nilOrEqual(id first, id second) { + return (first == nil) || ([first isEqual:second]); +} + @implementation HTMLDocumentType - (instancetype)initWithName:(NSString *)name @@ -22,6 +26,86 @@ return self; } +- (BOOL)isValid +{ + if (![self.name isEqualToString:@"html"]) { + return NO; + } + + if ([_publicIdentifier isEqualToString:@"-//W3C//DTD HTML 4.0//EN"] && + nilOrEqual(_systemIdentifier, @"http://www.w3.org/TR/REC-html40/strict.dtd")) { + return YES; + } + + if ([_publicIdentifier isEqualToString:@"-//W3C//DTD HTML 4.01//EN"] && + nilOrEqual(_systemIdentifier, @"http://www.w3.org/TR/html4/strict.dtd")) { + return YES; + } + + if ([_publicIdentifier isEqualToString:@"-//W3C//DTD XHTML 1.0 Strict//EN"] && + nilOrEqual(_systemIdentifier, @"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd")) { + return YES; + } + + if ([_publicIdentifier isEqualToString:@"-//W3C//DTD XHTML 1.1//EN"] && + nilOrEqual(_systemIdentifier, @"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd")) { + return YES; + } + + if (_publicIdentifier != nil) { + return NO; + } + + if (_systemIdentifier && ![_systemIdentifier isEqualToString:@"about:legacy-compat"]) { + return NO; + } + + return YES; +} + +- (HTMLQuirksMode)quirksMode +{ + if (![self.name isEqualToString:@"html"]) { + return HTMLQuirksModeQuirks; + } + + if (isEqualCaseInsensitive(_publicIdentifier, @"-//W3O//DTD W3 HTML Strict 3.0//EN//") || + isEqualCaseInsensitive(_publicIdentifier, @"-/W3C/DTD HTML 4.0 Transitional/EN") || + isEqualCaseInsensitive(_publicIdentifier, @"HTML")) { + return HTMLQuirksModeQuirks; + } + + if (isEqualCaseInsensitive(_systemIdentifier, @"http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd")) { + return HTMLQuirksModeQuirks; + } + + if (QuirksModePrefixMatch(_publicIdentifier)) { + return HTMLQuirksModeQuirks; + } + + if (_systemIdentifier == nil) { + if (hasPrefixCaseInsensitive(_publicIdentifier, @"-//W3C//DTD HTML 4.01 Frameset//") || + hasPrefixCaseInsensitive(_publicIdentifier, @"-//W3C//DTD HTML 4.01 Transitional//")) { + return HTMLQuirksModeQuirks; + } + } + +#warning Check "iframe srcdoc" + if (hasPrefixCaseInsensitive(_publicIdentifier, @"-//W3C//DTD XHTML 1.0 Frameset//") || + hasPrefixCaseInsensitive(_publicIdentifier, @"-//W3C//DTD XHTML 1.0 Transitional//")) { + return HTMLQuirksModeLimitedQuirks; + } + + if (_systemIdentifier != nil) { + if (hasPrefixCaseInsensitive(_publicIdentifier, @"-//W3C//DTD HTML 4.01 Frameset//") || + hasPrefixCaseInsensitive(_publicIdentifier, @"-//W3C//DTD HTML 4.01 Transitional//")) { + return HTMLQuirksModeLimitedQuirks; + } + } + + return HTMLQuirksModeNoQuirks; +} + #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone diff --git a/HTMLKit/HTMLQuirksMode.h b/HTMLKit/HTMLQuirksMode.h new file mode 100644 index 0000000..e5caf19 --- /dev/null +++ b/HTMLKit/HTMLQuirksMode.h @@ -0,0 +1,97 @@ +// +// HTMLQuirksMode.h +// HTMLKit +// +// Created by Iska on 28/02/15. +// Copyright (c) 2015 BrainCookie. All rights reserved. +// + +typedef NS_ENUM(short, HTMLQuirksMode) +{ + HTMLQuirksModeNoQuirks, + HTMLQuirksModeQuirks, + HTMLQuirksModeLimitedQuirks +}; + +#define QUIRKS_MODE_PREFIXES \ + QUIRKS_ENTRY( "+//Silmaril//dtd html Pro v0r11 19970101//" ) \ + QUIRKS_ENTRY( "-//AS//DTD HTML 3.0 asWedit + extensions//" ) \ + QUIRKS_ENTRY( "-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 2.0 Level 1//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 2.0 Level 2//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 2.0 Strict Level 1//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 2.0 Strict Level 2//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 2.0 Strict//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 2.0//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 2.1E//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 3.0//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 3.2 Final//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 3.2//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML 3//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Level 0//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Level 1//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Level 2//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Level 3//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Strict Level 0//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Strict Level 1//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Strict Level 2//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Strict Level 3//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML Strict//" ) \ + QUIRKS_ENTRY( "-//IETF//DTD HTML//" ) \ + QUIRKS_ENTRY( "-//Metrius//DTD Metrius Presentational//" ) \ + QUIRKS_ENTRY( "-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//" ) \ + QUIRKS_ENTRY( "-//Microsoft//DTD Internet Explorer 2.0 HTML//" ) \ + QUIRKS_ENTRY( "-//Microsoft//DTD Internet Explorer 2.0 Tables//" ) \ + QUIRKS_ENTRY( "-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//" ) \ + QUIRKS_ENTRY( "-//Microsoft//DTD Internet Explorer 3.0 HTML//" ) \ + QUIRKS_ENTRY( "-//Microsoft//DTD Internet Explorer 3.0 Tables//" ) \ + QUIRKS_ENTRY( "-//Netscape Comm. Corp.//DTD HTML//" ) \ + QUIRKS_ENTRY( "-//Netscape Comm. Corp.//DTD Strict HTML//" ) \ + QUIRKS_ENTRY( "-//O'Reilly and Associates//DTD HTML 2.0//" ) \ + QUIRKS_ENTRY( "-//O'Reilly and Associates//DTD HTML Extended 1.0//" ) \ + QUIRKS_ENTRY( "-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//" ) \ + QUIRKS_ENTRY( "-//SQ//DTD HTML 2.0 HoTMetaL + extensions//" ) \ + QUIRKS_ENTRY( "-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::extensions to HTML 4.0//" ) \ + QUIRKS_ENTRY( "-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::extensions to HTML 4.0//" ) \ + QUIRKS_ENTRY( "-//Spyglass//DTD HTML 2.0 Extended//" ) \ + QUIRKS_ENTRY( "-//Sun Microsystems Corp.//DTD HotJava HTML//" ) \ + QUIRKS_ENTRY( "-//Sun Microsystems Corp.//DTD HotJava Strict HTML//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML 3 1995-03-24//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML 3.2 Draft//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML 3.2 Final//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML 3.2//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML 3.2S Draft//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML 4.0 Frameset//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML 4.0 Transitional//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML Experimental 19960712//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD HTML Experimental 970421//" ) \ + QUIRKS_ENTRY( "-//W3C//DTD W3 HTML//" ) \ + QUIRKS_ENTRY( "-//W3O//DTD W3 HTML 3.0//" ) \ + QUIRKS_ENTRY( "-//WebTechs//DTD Mozilla HTML 2.0//" ) \ + QUIRKS_ENTRY( "-//WebTechs//DTD Mozilla HTML//" ) + +static NSString * HTMLQuirksModePrefixes[] = { +#define QUIRKS_ENTRY( prefix ) @prefix, + QUIRKS_MODE_PREFIXES +#undef QUIRKS_ENTRY +}; + +NS_INLINE BOOL isEqualCaseInsensitive(NSString *first, NSString *second) { + return [first caseInsensitiveCompare:second] == NSOrderedSame; +} + +NS_INLINE BOOL hasPrefixCaseInsensitive(NSString *string, NSString *prefix) { + NSRange reange = [string rangeOfString:prefix + options:NSAnchoredSearch|NSCaseInsensitiveSearch]; + return reange.location != NSNotFound; +} + +NS_INLINE BOOL QuirksModePrefixMatch(NSString *publicIdentifier) +{ + for (int i = 0; i < sizeof(HTMLQuirksModePrefixes) / sizeof(HTMLQuirksModePrefixes[0]); i++) { + if (hasPrefixCaseInsensitive(publicIdentifier, HTMLQuirksModePrefixes[i])) { + return YES; + } + } + return NO; +}