1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 
3 using System.IO;
4 using System.Net;
5 using System.Reflection;
6 using System.Text.RegularExpressions;
7 using System.Web.WebPages;
8 using System.Xml;
9 using System.Xml.Resolvers;
10 using Xunit;
11 
12 namespace System.Web.Helpers.Test
13 {
14     // see: http://msdn.microsoft.com/en-us/library/hdf992b8(v=VS.100).aspx
15     // see: http://blogs.msdn.com/xmlteam/archive/2008/08/14/introducing-the-xmlpreloadedresolver.aspx
16     public class XhtmlAssert
17     {
18         const string Xhtml10Wrapper = "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head><body>{0}</body></html>";
19         const string DOCTYPE_XHTML1_1 = "<!DOCTYPE {0} PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"xhtml11-flat.dtd\">\r\n";
20 
Validate1_0(object result, bool addRoot = false)21         public static void Validate1_0(object result, bool addRoot = false)
22         {
23             string html = null;
24             if (addRoot)
25             {
26                 html = String.Format(Xhtml10Wrapper, GetHtml(result));
27             }
28             else
29             {
30                 html = GetHtml(result);
31             }
32 
33             Validate1_0(html);
34         }
35 
Validate1_1(object result, string wrapper = null)36         public static void Validate1_1(object result, string wrapper = null)
37         {
38             string root;
39             string html = GetHtml(result);
40             if (String.IsNullOrEmpty(wrapper))
41             {
42                 root = GetRoot(html);
43             }
44             else
45             {
46                 root = wrapper;
47                 html = String.Format("<{0}>{1}</{0}>", wrapper, html);
48             }
49             Validate1_1(root, html);
50         }
51 
GetHtml(object result)52         private static string GetHtml(object result)
53         {
54             Assert.True((result is IHtmlString) || (result is HelperResult), "Helpers should return IHTMLString or HelperResult");
55             return result.ToString();
56         }
57 
GetRoot(string html)58         private static string GetRoot(string html)
59         {
60             Regex regex = new Regex(@"<(\w+)[\s>]");
61             Match match = regex.Match(html);
62             Assert.True(match.Success, "Could not determine root element");
63             Assert.True(match.Groups.Count > 1, "Could not determine root element");
64             return match.Groups[1].Value;
65         }
66 
Validate1_0(string html)67         private static void Validate1_0(string html)
68         {
69             XmlReaderSettings settings = new XmlReaderSettings();
70             settings.DtdProcessing = DtdProcessing.Parse;
71             settings.XmlResolver = new XmlPreloadedResolver(XmlKnownDtds.Xhtml10);
72 
73             Validate(settings, html);
74         }
75 
Validate1_1(string root, string html)76         private static void Validate1_1(string root, string html)
77         {
78             var settings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse, ValidationType = ValidationType.DTD, XmlResolver = new AssemblyResourceXmlResolver() };
79 
80             string docType = String.Format(DOCTYPE_XHTML1_1, root);
81             Validate(settings, docType + html);
82         }
83 
Validate(XmlReaderSettings settings, string html)84         private static void Validate(XmlReaderSettings settings, string html)
85         {
86             using (StringReader sr = new StringReader(html))
87             {
88                 using (XmlReader reader = XmlReader.Create(sr, settings))
89                 {
90                     while (reader.Read())
91                     {
92                         // XHTML element and attribute names must be lowercase, since XML is case sensitive.
93                         // The W3C validator detects this, but we must manually check since the XmlReader does not.
94                         // See: http://www.w3.org/TR/xhtml1/#h-4.2
95                         if (reader.NodeType == XmlNodeType.Element)
96                         {
97                             string element = reader.Name;
98                             Assert.True(element == element.ToLowerInvariant());
99                             if (reader.HasAttributes)
100                             {
101                                 for (int i = 0; i < reader.AttributeCount; i++)
102                                 {
103                                     reader.MoveToAttribute(i);
104                                     string attribute = reader.Name;
105                                     Assert.True(attribute == attribute.ToLowerInvariant());
106                                 }
107                                 // move back to element node
108                                 reader.MoveToElement();
109                             }
110                         }
111                     }
112                 }
113             }
114         }
115 
116         private class AssemblyResourceXmlResolver : XmlResolver
117         {
118             public override ICredentials Credentials
119             {
120                 set { throw new NotSupportedException(); }
121             }
122 
GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)123             public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
124             {
125                 Assembly assembly = typeof(XhtmlAssert).Assembly;
126                 return assembly.GetManifestResourceStream("System.Web.Helpers.Test.TestFiles.xhtml11-flat.dtd");
127             }
128         }
129     }
130 }
131