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;
+}