1 // Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
2 
3 using System.ComponentModel;
4 using System.Diagnostics;
5 using System.Diagnostics.CodeAnalysis;
6 using System.Globalization;
7 using System.IO;
8 using System.Web.WebPages.Instrumentation;
9 using System.Web.WebPages.Resources;
10 
11 /*
12 WebPage class hierarchy
13 
14 WebPageExecutingBase                        The base class for all Plan9 files (_pagestart, _appstart, and regular pages)
15     ApplicationStartPage                    Used for _appstart.cshtml
16     WebPageRenderingBase
17         StartPage                           Used for _pagestart.cshtml
18         WebPageBase
19             WebPage                         Plan9Pages
20             ViewWebPage?                    MVC Views
21 HelperPage                                  Base class for Web Pages in App_Code.
22 */
23 
24 namespace System.Web.WebPages
25 {
26     // The base class for all CSHTML files (_pagestart, _appstart, and regular pages)
27     public abstract class WebPageExecutingBase
28     {
29         private IVirtualPathFactory _virtualPathFactory;
30         private DynamicHttpApplicationState _dynamicAppState;
31         private InstrumentationService _instrumentationService = null;
32 
33         internal InstrumentationService InstrumentationService
34         {
35             get
36             {
37                 if (_instrumentationService == null)
38                 {
39                     _instrumentationService = new InstrumentationService();
40                 }
41                 return _instrumentationService;
42             }
43             set { _instrumentationService = value; }
44         }
45 
46         public virtual HttpApplicationStateBase AppState
47         {
48             get
49             {
50                 if (Context != null)
51                 {
52                     return Context.Application;
53                 }
54                 return null;
55             }
56         }
57 
58         public virtual dynamic App
59         {
60             get
61             {
62                 if (_dynamicAppState == null && AppState != null)
63                 {
64                     _dynamicAppState = new DynamicHttpApplicationState(AppState);
65                 }
66                 return _dynamicAppState;
67             }
68         }
69 
70         public virtual HttpContextBase Context { get; set; }
71 
72         public virtual string VirtualPath { get; set; }
73 
74         [EditorBrowsable(EditorBrowsableState.Never)]
75         public virtual IVirtualPathFactory VirtualPathFactory
76         {
77             get { return _virtualPathFactory ?? VirtualPathFactoryManager.Instance; }
78             set { _virtualPathFactory = value; }
79         }
80 
81         [EditorBrowsable(EditorBrowsableState.Never)]
Execute()82         public abstract void Execute();
83 
Href(string path, params object[] pathParts)84         public virtual string Href(string path, params object[] pathParts)
85         {
86             return UrlUtil.Url(VirtualPath, path, pathParts);
87         }
88 
BeginContext(int startPosition, int length, bool isLiteral)89         protected internal void BeginContext(int startPosition, int length, bool isLiteral)
90         {
91             BeginContext(GetOutputWriter(), VirtualPath, startPosition, length, isLiteral);
92         }
93 
BeginContext(string virtualPath, int startPosition, int length, bool isLiteral)94         protected internal void BeginContext(string virtualPath, int startPosition, int length, bool isLiteral)
95         {
96             BeginContext(GetOutputWriter(), virtualPath, startPosition, length, isLiteral);
97         }
98 
BeginContext(TextWriter writer, int startPosition, int length, bool isLiteral)99         protected internal void BeginContext(TextWriter writer, int startPosition, int length, bool isLiteral)
100         {
101             BeginContext(writer, VirtualPath, startPosition, length, isLiteral);
102         }
103 
BeginContext(TextWriter writer, string virtualPath, int startPosition, int length, bool isLiteral)104         protected internal void BeginContext(TextWriter writer, string virtualPath, int startPosition, int length, bool isLiteral)
105         {
106             // Double check that the instrumentation service is active because WriteAttribute always calls this
107             if (InstrumentationService.IsAvailable)
108             {
109                 InstrumentationService.BeginContext(Context,
110                                                     virtualPath,
111                                                     writer,
112                                                     startPosition,
113                                                     length,
114                                                     isLiteral);
115             }
116         }
117 
EndContext(int startPosition, int length, bool isLiteral)118         protected internal void EndContext(int startPosition, int length, bool isLiteral)
119         {
120             EndContext(GetOutputWriter(), VirtualPath, startPosition, length, isLiteral);
121         }
122 
EndContext(string virtualPath, int startPosition, int length, bool isLiteral)123         protected internal void EndContext(string virtualPath, int startPosition, int length, bool isLiteral)
124         {
125             EndContext(GetOutputWriter(), virtualPath, startPosition, length, isLiteral);
126         }
127 
EndContext(TextWriter writer, int startPosition, int length, bool isLiteral)128         protected internal void EndContext(TextWriter writer, int startPosition, int length, bool isLiteral)
129         {
130             EndContext(writer, VirtualPath, startPosition, length, isLiteral);
131         }
132 
EndContext(TextWriter writer, string virtualPath, int startPosition, int length, bool isLiteral)133         protected internal void EndContext(TextWriter writer, string virtualPath, int startPosition, int length, bool isLiteral)
134         {
135             // Double check that the instrumentation service is active because WriteAttribute always calls this
136             if (InstrumentationService.IsAvailable)
137             {
138                 InstrumentationService.EndContext(Context,
139                                                   virtualPath,
140                                                   writer,
141                                                   startPosition,
142                                                   length,
143                                                   isLiteral);
144             }
145         }
146 
GetDirectory(string virtualPath)147         internal virtual string GetDirectory(string virtualPath)
148         {
149             return VirtualPathUtility.GetDirectory(virtualPath);
150         }
151 
152         /// <summary>
153         /// Normalizes path relative to the current virtual path and throws if a file does not exist at the location.
154         /// </summary>
NormalizeLayoutPagePath(string layoutPagePath)155         internal string NormalizeLayoutPagePath(string layoutPagePath)
156         {
157             var virtualPath = NormalizePath(layoutPagePath);
158             // Look for it as specified, either absolute, relative or same folder
159             if (VirtualPathFactory.Exists(virtualPath))
160             {
161                 return virtualPath;
162             }
163             throw new HttpException(String.Format(CultureInfo.CurrentCulture, WebPageResources.WebPage_LayoutPageNotFound, layoutPagePath, virtualPath));
164         }
165 
NormalizePath(string path)166         public virtual string NormalizePath(string path)
167         {
168             // If it's relative, resolve it
169             return VirtualPathUtility.Combine(VirtualPath, path);
170         }
171 
Write(HelperResult result)172         public abstract void Write(HelperResult result);
173 
Write(object value)174         public abstract void Write(object value);
175 
WriteLiteral(object value)176         public abstract void WriteLiteral(object value);
177 
WriteAttribute(string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values)178         public virtual void WriteAttribute(string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values)
179         {
180             WriteAttributeTo(GetOutputWriter(), name, prefix, suffix, values);
181         }
182 
WriteAttributeTo(TextWriter writer, string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values)183         public virtual void WriteAttributeTo(TextWriter writer, string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values)
184         {
185             WriteAttributeTo(VirtualPath, writer, name, prefix, suffix, values);
186         }
187 
WriteAttributeTo(string pageVirtualPath, TextWriter writer, string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values)188         protected internal virtual void WriteAttributeTo(string pageVirtualPath, TextWriter writer, string name, PositionTagged<string> prefix, PositionTagged<string> suffix, params AttributeValue[] values)
189         {
190             bool first = true;
191             bool wroteSomething = false;
192             if (values.Length == 0)
193             {
194                 // Explicitly empty attribute, so write the prefix and suffix
195                 WritePositionTaggedLiteral(writer, pageVirtualPath, prefix);
196                 WritePositionTaggedLiteral(writer, pageVirtualPath, suffix);
197             }
198             else
199             {
200                 foreach (AttributeValue attrVal in values)
201                 {
202                     PositionTagged<object> val = attrVal.Value;
203                     bool? boolVal = null;
204                     if (val.Value is bool)
205                     {
206                         boolVal = (bool)val.Value;
207                     }
208 
209                     if (val.Value != null && (boolVal == null || boolVal.Value))
210                     {
211                         string valStr = val.Value as string;
212                         if (valStr == null)
213                         {
214                             valStr = val.Value.ToString();
215                         }
216                         if (boolVal != null)
217                         {
218                             Debug.Assert(boolVal.Value);
219                             valStr = name;
220                         }
221 
222                         if (first)
223                         {
224                             WritePositionTaggedLiteral(writer, pageVirtualPath, prefix);
225                             first = false;
226                         }
227                         else
228                         {
229                             WritePositionTaggedLiteral(writer, pageVirtualPath, attrVal.Prefix);
230                         }
231                         BeginContext(writer, pageVirtualPath, attrVal.Value.Position, valStr.Length, isLiteral: attrVal.Literal);
232                         if (attrVal.Literal)
233                         {
234                             WriteLiteralTo(writer, valStr);
235                         }
236                         else
237                         {
238                             WriteTo(writer, valStr); // Write value
239                         }
240                         EndContext(writer, pageVirtualPath, attrVal.Value.Position, valStr.Length, isLiteral: attrVal.Literal);
241                         wroteSomething = true;
242                     }
243                 }
244                 if (wroteSomething)
245                 {
246                     WritePositionTaggedLiteral(writer, pageVirtualPath, suffix);
247                 }
248             }
249         }
250 
WritePositionTaggedLiteral(TextWriter writer, string pageVirtualPath, string value, int position)251         private void WritePositionTaggedLiteral(TextWriter writer, string pageVirtualPath, string value, int position)
252         {
253             BeginContext(writer, pageVirtualPath, position, value.Length, isLiteral: true);
254             WriteLiteralTo(writer, value);
255             EndContext(writer, pageVirtualPath, position, value.Length, isLiteral: true);
256         }
257 
WritePositionTaggedLiteral(TextWriter writer, string pageVirtualPath, PositionTagged<string> value)258         private void WritePositionTaggedLiteral(TextWriter writer, string pageVirtualPath, PositionTagged<string> value)
259         {
260             WritePositionTaggedLiteral(writer, pageVirtualPath, value.Value, value.Position);
261         }
262 
263         // This method is called by generated code and needs to stay in sync with the parser
WriteTo(TextWriter writer, HelperResult content)264         public static void WriteTo(TextWriter writer, HelperResult content)
265         {
266             if (content != null)
267             {
268                 content.WriteTo(writer);
269             }
270         }
271 
272         // This method is called by generated code and needs to stay in sync with the parser
WriteTo(TextWriter writer, object content)273         public static void WriteTo(TextWriter writer, object content)
274         {
275             writer.Write(HttpUtility.HtmlEncode(content));
276         }
277 
278         // This method is called by generated code and needs to stay in sync with the parser
WriteLiteralTo(TextWriter writer, object content)279         public static void WriteLiteralTo(TextWriter writer, object content)
280         {
281             writer.Write(content);
282         }
283 
284         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "A method is more appropriate in this case since a property likely already exists to hold this value")]
GetOutputWriter()285         protected internal virtual TextWriter GetOutputWriter()
286         {
287             return TextWriter.Null;
288         }
289     }
290 }
291