Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,228 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.Routing;
using System.Web.WebPages.ApplicationParts;
using System.Web.WebPages.Resources;
using Microsoft.Internal.Web.Utils;
namespace System.Web.WebPages
{
public class ApplicationPart
{
private const string ModuleRootSyntax = "@/";
private const string ResourceVirtualPathRoot = "~/r.ashx/";
private const string ResourceRoute = "r.ashx/{module}/{*path}";
private static readonly LazyAction _initApplicationPart = new LazyAction(InitApplicationParts);
private static ApplicationPartRegistry _partRegistry;
private readonly Lazy<IDictionary<string, string>> _applicationPartResources;
private readonly Lazy<string> _applicationPartName;
public ApplicationPart(Assembly assembly, string rootVirtualPath)
: this(new ResourceAssembly(assembly), rootVirtualPath)
{
}
internal ApplicationPart(IResourceAssembly assembly, string rootVirtualPath)
{
if (String.IsNullOrEmpty(rootVirtualPath))
{
throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "rootVirtualPath");
}
// Make sure the root path ends with a slash
if (!rootVirtualPath.EndsWith("/", StringComparison.Ordinal))
{
rootVirtualPath += "/";
}
Assembly = assembly;
RootVirtualPath = rootVirtualPath;
_applicationPartResources = new Lazy<IDictionary<string, string>>(() => Assembly.GetManifestResourceNames().ToDictionary(key => key, key => key, StringComparer.OrdinalIgnoreCase));
_applicationPartName = new Lazy<string>(() => Assembly.Name);
}
internal IResourceAssembly Assembly { get; private set; }
internal string RootVirtualPath { get; private set; }
internal string Name
{
get { return _applicationPartName.Value; }
}
internal IDictionary<string, string> ApplicationPartResources
{
get { return _applicationPartResources.Value; }
}
// REVIEW: Do we need an Unregister?
// Register an assembly as an application module, which makes its compiled web pages
// and embedded resources available
public static void Register(ApplicationPart applicationPart)
{
// Ensure the registry is ready and the route handlers are set up
_initApplicationPart.EnsurePerformed();
Debug.Assert(_partRegistry != null, "Part registry should be initialized");
_partRegistry.Register(applicationPart);
}
public static string ProcessVirtualPath(Assembly assembly, string baseVirtualPath, string virtualPath)
{
if (_partRegistry == null)
{
// This was called without registering a part.
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, WebPageResources.ApplicationPart_ModuleNotRegistered, assembly));
}
ApplicationPart applicationPart = _partRegistry[new ResourceAssembly(assembly)];
if (applicationPart == null)
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
WebPageResources.ApplicationPart_ModuleNotRegistered,
assembly));
}
return applicationPart.ProcessVirtualPath(baseVirtualPath, virtualPath);
}
internal static IEnumerable<ApplicationPart> GetRegisteredParts()
{
_initApplicationPart.EnsurePerformed();
return _partRegistry.RegisteredParts;
}
private string ProcessVirtualPath(string baseVirtualPath, string virtualPath)
{
virtualPath = ResolveVirtualPath(RootVirtualPath, baseVirtualPath, virtualPath);
if (!virtualPath.StartsWith(RootVirtualPath, StringComparison.OrdinalIgnoreCase))
{
return virtualPath;
}
// Remove the root package path from the path, since the resource name doesn't use it
// e.g. ~/admin/Debugger/Sub Folder/foo.jpg ==> ~/Sub Folder/foo.jpg
string packageVirtualPath = "~/" + virtualPath.Substring(RootVirtualPath.Length);
string resourceName = GetResourceNameFromVirtualPath(packageVirtualPath);
// If the assembly doesn't contains that resource, don't change the path
if (!ApplicationPartResources.ContainsKey(resourceName))
{
return virtualPath;
}
// The resource exists, so return a special path that will be handled by the resource handler
return GetResourceVirtualPath(virtualPath);
}
/// <summary>
/// Expands a virtual path by replacing a leading "@" with the application part root
/// or combining it with the specified baseVirtualPath
/// </summary>
internal static string ResolveVirtualPath(string applicationRoot, string baseVirtualPath, string virtualPath)
{
// If it starts with @/, replace that with the package root
// e.g. @/Sub Folder/foo.jpg ==> ~/admin/Debugger/Sub Folder/foo.jpg
if (virtualPath.StartsWith(ModuleRootSyntax, StringComparison.OrdinalIgnoreCase))
{
return applicationRoot + virtualPath.Substring(ModuleRootSyntax.Length);
}
else
{
// Resolve if relative to the base
return VirtualPathUtility.Combine(baseVirtualPath, virtualPath);
}
}
internal Stream GetResourceStream(string virtualPath)
{
string resourceName = GetResourceNameFromVirtualPath(virtualPath);
string normalizedResourceName;
if (ApplicationPartResources.TryGetValue(resourceName, out normalizedResourceName))
{
// Return the resource stream
return Assembly.GetManifestResourceStream(normalizedResourceName);
}
return null;
}
// Get the name of an embedded resource based on a virtual path
private string GetResourceNameFromVirtualPath(string virtualPath)
{
return GetResourceNameFromVirtualPath(Name, virtualPath);
}
internal static string GetResourceNameFromVirtualPath(string moduleName, string virtualPath)
{
// Make sure path starts with ~/
if (!virtualPath.StartsWith("~/", StringComparison.Ordinal))
{
virtualPath = "~/" + virtualPath;
}
// Get the directory part of the path
// e.g. ~/Sub Folder/foo.jpg ==> ~/Sub Folder/
string dir = VirtualPathUtility.GetDirectory(virtualPath);
// Get rid of the starting ~/
// e.g. ~/Sub Folder/ ==> Sub Folder/
if (dir.Length >= 2)
{
dir = dir.Substring(2);
}
// Replace / with . and spaces with _
// TODO: other special chars need to be replaced by _ as well
// e.g. Sub Folder/ ==> Sub_Folder.
dir = dir.Replace('/', '.');
dir = dir.Replace(' ', '_');
// Get the file name part. That part of the resource names is the same as in the virtual path,
// so no replacements are needed
// e.g. ~/Sub Folder/foo.jpg ==> foo.jpg
string fileName = Path.GetFileName(virtualPath);
// Put them back together, and prepend the assembly name
// e.g. DebuggerAssembly.Sub_Folder.foo.jpg
return moduleName + "." + dir + fileName;
}
// Get a virtual path that uses the resource handler from a regular virtual path
private string GetResourceVirtualPath(string virtualPath)
{
return GetResourceVirtualPath(Name, RootVirtualPath, virtualPath);
}
internal static string GetResourceVirtualPath(string moduleName, string moduleRoot, string virtualPath)
{
// The path should always start with the root of the module. Skip it.
Debug.Assert(virtualPath.StartsWith(moduleRoot, StringComparison.OrdinalIgnoreCase));
virtualPath = virtualPath.Substring(moduleRoot.Length).TrimStart('/');
// Make a path to the resource through our resource route, e.g. ~/r.ashx/sub/foo.jpg
// e.g. ~/admin/Debugger/Sub Folder/foo.jpg ==> ~/r.ashx/DebuggerPackageName/Sub Folder/foo.jpg
return ResourceVirtualPathRoot + HttpUtility.UrlPathEncode(moduleName) + "/" + virtualPath;
}
private static void InitApplicationParts()
{
// Register the virtual path factory
var virtualPathFactory = new DictionaryBasedVirtualPathFactory();
VirtualPathFactoryManager.RegisterVirtualPathFactory(virtualPathFactory);
// Intantiate the part registry
_partRegistry = new ApplicationPartRegistry(virtualPathFactory);
// Register the resource route
RouteTable.Routes.Add(new Route(ResourceRoute, new ResourceRouteHandler(_partRegistry)));
}
}
}

View File

@ -0,0 +1,144 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Web.WebPages.Resources;
namespace System.Web.WebPages.ApplicationParts
{
internal class ApplicationPartRegistry
{
// Page types that we could serve
private static readonly Type _webPageType = typeof(WebPageRenderingBase);
private readonly DictionaryBasedVirtualPathFactory _virtualPathFactory;
private readonly ConcurrentDictionary<string, bool> _registeredVirtualPaths;
private readonly ConcurrentDictionary<IResourceAssembly, ApplicationPart> _applicationParts;
public ApplicationPartRegistry(DictionaryBasedVirtualPathFactory pathFactory)
{
_applicationParts = new ConcurrentDictionary<IResourceAssembly, ApplicationPart>();
_registeredVirtualPaths = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
_virtualPathFactory = pathFactory;
}
public IEnumerable<ApplicationPart> RegisteredParts
{
get { return _applicationParts.Values; }
}
public ApplicationPart this[string name]
{
get { return _applicationParts.Values.FirstOrDefault(appPart => appPart.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); }
}
public ApplicationPart this[IResourceAssembly assembly]
{
get
{
ApplicationPart part;
if (!_applicationParts.TryGetValue(assembly, out part))
{
part = null;
}
return part;
}
}
// Register an assembly as an application module, which makes its compiled web pages
// and embedded resources available
public void Register(ApplicationPart applicationPart)
{
Register(applicationPart, registerPageAction: null); // Use default action which creates a new page
}
// Register an assembly as an application module, which makes its compiled web pages
// and embedded resources available
internal void Register(ApplicationPart applicationPart, Func<object> registerPageAction)
{
// Throw if this assembly has been registered
if (_applicationParts.ContainsKey(applicationPart.Assembly))
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
WebPageResources.ApplicationPart_ModuleAlreadyRegistered, applicationPart.Assembly));
}
// Throw if the virtual path is already in use
if (_registeredVirtualPaths.ContainsKey(applicationPart.RootVirtualPath))
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
WebPageResources.ApplicationPart_ModuleAlreadyRegisteredForVirtualPath, applicationPart.RootVirtualPath));
}
// REVIEW: Should we register the app-part after we scan the assembly for webpages?
// Add the part to the list
if (_applicationParts.TryAdd(applicationPart.Assembly, applicationPart))
{
// We don't really care about the value
_registeredVirtualPaths.TryAdd(applicationPart.RootVirtualPath, true);
// Get all of the web page types
var webPageTypes = from type in applicationPart.Assembly.GetTypes()
where type.IsSubclassOf(_webPageType)
select type;
// Register each of page with the plan9
foreach (Type webPageType in webPageTypes)
{
RegisterWebPage(applicationPart, webPageType, registerPageAction);
}
}
}
private void RegisterWebPage(ApplicationPart module, Type webPageType, Func<object> registerPageAction)
{
var virtualPathAttribute = webPageType.GetCustomAttributes(typeof(PageVirtualPathAttribute), false)
.Cast<PageVirtualPathAttribute>()
.SingleOrDefault();
// Ignore it if it doesn't have a PageVirtualPathAttribute
if (virtualPathAttribute == null)
{
return;
}
// Get the path of the page relative to the module root
string virtualPath = GetRootRelativeVirtualPath(module.RootVirtualPath, virtualPathAttribute.VirtualPath);
// Create a factory for the page type
Func<object> pageFactory = registerPageAction ?? NewTypeInstance(webPageType);
// Register a page factory for it
_virtualPathFactory.RegisterPath(virtualPath, pageFactory);
}
private static Func<object> NewTypeInstance(Type type)
{
return Expression.Lambda<Func<object>>(Expression.New(type)).Compile();
}
internal static string GetRootRelativeVirtualPath(string rootVirtualPath, string pageVirtualPath)
{
string virtualPath = pageVirtualPath;
// Trim the ~/ prefix, since we want it to be relative to the module base path
if (virtualPath.StartsWith("~/", StringComparison.Ordinal))
{
virtualPath = virtualPath.Substring(2);
}
if (!rootVirtualPath.EndsWith("/", StringComparison.OrdinalIgnoreCase))
{
rootVirtualPath += "/";
}
// Combine it with the root
virtualPath = VirtualPathUtility.Combine(rootVirtualPath, virtualPath);
return virtualPath;
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
namespace System.Web.WebPages.ApplicationParts
{
// IVirtualPathFactory that keeps track of a mapping from virtual paths to handler factories
internal class DictionaryBasedVirtualPathFactory : IVirtualPathFactory
{
private Dictionary<string, Func<object>> _factories = new Dictionary<string, Func<object>>(StringComparer.OrdinalIgnoreCase);
internal void RegisterPath(string virtualPath, Func<object> factory)
{
_factories[virtualPath] = factory;
}
public bool Exists(string virtualPath)
{
return _factories.ContainsKey(virtualPath);
}
public object CreateInstance(string virtualPath)
{
// Instantiate an object for the path
// Note that this fails if it doesn't exist. PathExists is assumed to have been called first
Debug.Assert(Exists(virtualPath));
return _factories[virtualPath]();
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.IO;
namespace System.Web.WebPages.ApplicationParts
{
// For unit testing purpose since Assembly is not Moqable
internal interface IResourceAssembly
{
string Name { get; }
Stream GetManifestResourceStream(string name);
IEnumerable<string> GetManifestResourceNames();
IEnumerable<Type> GetTypes();
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics;
namespace System.Web.WebPages.ApplicationParts
{
internal class LazyAction
{
private Lazy<object> _lazyAction;
public LazyAction(Action action)
{
Debug.Assert(action != null, "action should not be null");
// Setup the lazy object to run our action and just return null
// since we don't care about the value
_lazyAction = new Lazy<object>(() =>
{
action();
return null;
});
}
public object EnsurePerformed()
{
// REVIEW: This isn't used we're just exploiting the use of Lazy<T> to execute
// our action once in a thread safe way
// It would be nice if the framework had Unit (i.e a better void type so we could type Func<Unit> -> Action)
return _lazyAction.Value;
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace System.Web.WebPages.ApplicationParts
{
// Implementation of IResourceAssembly over a standard assembly
internal class ResourceAssembly : IResourceAssembly
{
private readonly Assembly _assembly;
public ResourceAssembly(Assembly assembly)
{
if (assembly == null)
{
throw new ArgumentNullException("assembly");
}
_assembly = assembly;
}
public string Name
{
get
{
// Need this for medium trust
AssemblyName assemblyName = new AssemblyName(_assembly.FullName);
Debug.Assert(assemblyName != null, "Assembly name should not be null");
// Use the assembly short name
return assemblyName.Name;
}
}
public Stream GetManifestResourceStream(string name)
{
return _assembly.GetManifestResourceStream(name);
}
public IEnumerable<string> GetManifestResourceNames()
{
return _assembly.GetManifestResourceNames();
}
public IEnumerable<Type> GetTypes()
{
return _assembly.GetExportedTypes();
}
public override bool Equals(object obj)
{
var otherAssembly = obj as ResourceAssembly;
return otherAssembly != null && otherAssembly._assembly.Equals(_assembly);
}
public override int GetHashCode()
{
return _assembly.GetHashCode();
}
public override String ToString()
{
return _assembly.ToString();
}
}
}

View File

@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Globalization;
using System.Web.WebPages.Resources;
using Microsoft.Internal.Web.Utils;
namespace System.Web.WebPages.ApplicationParts
{
// Used to serve static resource files (e.g. .jpg, .css, .js) that live inside appliaction modules
internal class ResourceHandler : IHttpHandler
{
private readonly string _path;
private readonly ApplicationPart _applicationPart;
public ResourceHandler(ApplicationPart applicationPart, string path)
{
if (applicationPart == null)
{
throw new ArgumentNullException("applicationPart");
}
if (String.IsNullOrEmpty(path))
{
throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "path");
}
_applicationPart = applicationPart;
_path = path;
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
ProcessRequest(new HttpResponseWrapper(context.Response));
}
internal void ProcessRequest(HttpResponseBase response)
{
string virtualPath = _path;
// Make sure it starts with ~/
if (!virtualPath.StartsWith("~/", StringComparison.Ordinal))
{
virtualPath = "~/" + virtualPath;
}
// Get the resource stream for this virtual path
using (var stream = _applicationPart.GetResourceStream(virtualPath))
{
if (stream == null)
{
throw new HttpException(404, String.Format(
CultureInfo.CurrentCulture,
WebPageResources.ApplicationPart_ResourceNotFound, _path));
}
// Set the mime type based on the file extension
response.ContentType = MimeMapping.GetMimeMapping(virtualPath);
// Copy it to the response
stream.CopyTo(response.OutputStream);
}
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Globalization;
using System.Web.Routing;
using System.Web.WebPages.Resources;
namespace System.Web.WebPages.ApplicationParts
{
internal class ResourceRouteHandler : IRouteHandler
{
private ApplicationPartRegistry _partRegistry;
public ResourceRouteHandler(ApplicationPartRegistry partRegistry)
{
_partRegistry = partRegistry;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// Get the package name and static resource path from the route
string partName = (string)requestContext.RouteData.GetRequiredString("module");
// Try to find an application module by this name
ApplicationPart module = _partRegistry[partName];
// Throw an exception if we can't find the module by name
if (module == null)
{
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentCulture,
WebPageResources.ApplicationPart_ModuleCannotBeFound, partName));
}
// Get the resource path
string path = (string)requestContext.RouteData.GetRequiredString("path");
// Return the resource handler for this module and path
return new ResourceHandler(module, path);
}
}
}

View File

@ -0,0 +1,175 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Web.Caching;
using System.Web.Hosting;
using Microsoft.Web.Infrastructure;
namespace System.Web.WebPages
{
public abstract class ApplicationStartPage : WebPageExecutingBase
{
private static readonly Action<Action> _safeExecuteStartPageThunk = GetSafeExecuteStartPageThunk();
public static readonly string StartPageVirtualPath = "~/_appstart.";
public static readonly string CacheKeyPrefix = "__AppStartPage__";
public HttpApplication Application { get; internal set; }
public override HttpContextBase Context
{
get { return new HttpContextWrapper(Application.Context); }
}
public static HtmlString Markup { get; private set; }
internal static Exception Exception { get; private set; }
public TextWriter Output { get; internal set; }
public override string VirtualPath
{
get { return StartPageVirtualPath; }
set
{
// The virtual path for the start page is fixed for now.
throw new NotSupportedException();
}
}
internal void ExecuteInternal()
{
// See comments in GetSafeExecuteStartPageThunk().
_safeExecuteStartPageThunk(() =>
{
Output = new StringWriter(CultureInfo.InvariantCulture);
Execute();
Markup = new HtmlString(Output.ToString());
});
}
internal static void ExecuteStartPage(HttpApplication application)
{
ExecuteStartPage(application,
vpath => MonitorFile(vpath),
VirtualPathFactoryManager.Instance,
WebPageHttpHandler.GetRegisteredExtensions());
}
internal static void ExecuteStartPage(HttpApplication application, Action<string> monitorFile, IVirtualPathFactory virtualPathFactory, IEnumerable<string> supportedExtensions)
{
try
{
ExecuteStartPageInternal(application, monitorFile, virtualPathFactory, supportedExtensions);
}
catch (Exception e)
{
// Throw it as a HttpException so as to
// display the original stack trace information.
Exception = e;
throw new HttpException(null, e);
}
}
internal static void ExecuteStartPageInternal(HttpApplication application, Action<string> monitorFile, IVirtualPathFactory virtualPathFactory, IEnumerable<string> supportedExtensions)
{
ApplicationStartPage startPage = null;
foreach (var extension in supportedExtensions)
{
var vpath = StartPageVirtualPath + extension;
// We need to monitor regardless of existence because the user could add/remove the
// file at any time.
monitorFile(vpath);
if (!virtualPathFactory.Exists(vpath))
{
continue;
}
if (startPage == null)
{
startPage = virtualPathFactory.CreateInstance<ApplicationStartPage>(vpath);
startPage.Application = application;
startPage.VirtualPathFactory = virtualPathFactory;
startPage.ExecuteInternal();
}
}
}
private static Action<Action> GetSafeExecuteStartPageThunk()
{
// Programmatically detect if this version of System.Web.dll suffers from a bug in
// which HttpUtility.HtmlEncode can't be called from Application_Start, and if so
// set the current HttpContext to null to work around it.
//
// See Dev10 #906296 and Dev10 #898600 for more information.
if (typeof(HttpResponse).GetProperty("DisableCustomHttpEncoder", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly) != null)
{
// this version suffers from the bug
return HttpContextHelper.ExecuteInNullContext;
}
else
{
// this version does not suffer from the bug
return action => action();
}
}
private static void InitiateShutdown(string key, object value, CacheItemRemovedReason reason)
{
// Only handle case when the dependency has changed.
if (reason != CacheItemRemovedReason.DependencyChanged)
{
return;
}
ThreadPool.QueueUserWorkItem(new WaitCallback(ShutdownCallBack));
}
private static void MonitorFile(string virtualPath)
{
var virtualPathDependencies = new List<string>();
virtualPathDependencies.Add(virtualPath);
CacheDependency cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(
virtualPath, virtualPathDependencies, DateTime.UtcNow);
var key = CacheKeyPrefix + virtualPath;
HttpRuntime.Cache.Insert(key, virtualPath, cacheDependency,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(InitiateShutdown));
}
private static void ShutdownCallBack(object state)
{
InfrastructureHelper.UnloadAppDomain();
}
public override void Write(HelperResult result)
{
if (result != null)
{
result.WriteTo(Output);
}
}
public override void WriteLiteral(object value)
{
Output.Write(value);
}
public override void Write(object value)
{
Output.Write(HttpUtility.HtmlEncode(value));
}
protected internal override TextWriter GetOutputWriter()
{
return Output;
}
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Web.WebPages.Instrumentation;
namespace System.Web.WebPages
{
public class AttributeValue
{
public AttributeValue(PositionTagged<string> prefix, PositionTagged<object> value, bool literal)
{
Prefix = prefix;
Value = value;
Literal = literal;
}
public PositionTagged<string> Prefix { get; private set; }
public PositionTagged<object> Value { get; private set; }
public bool Literal { get; private set; }
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "We are using tuples here to avoid dependencies from Razor to WebPages")]
public static AttributeValue FromTuple(Tuple<Tuple<string, int>, Tuple<object, int>, bool> value)
{
return new AttributeValue(value.Item1, value.Item2, value.Item3);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "We are using tuples here to avoid dependencies from Razor to WebPages")]
public static AttributeValue FromTuple(Tuple<Tuple<string, int>, Tuple<string, int>, bool> value)
{
return new AttributeValue(value.Item1, new PositionTagged<object>(value.Item2.Item1, value.Item2.Item2), value.Item3);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "We are using tuples here to avoid dependencies from Razor to WebPages")]
public static implicit operator AttributeValue(Tuple<Tuple<string, int>, Tuple<object, int>, bool> value)
{
return FromTuple(value);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "We are using tuples here to avoid dependencies from Razor to WebPages")]
public static implicit operator AttributeValue(Tuple<Tuple<string, int>, Tuple<string, int>, bool> value)
{
return FromTuple(value);
}
}
}

View File

@ -0,0 +1,170 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Web.Hosting;
namespace System.Web.WebPages
{
/// <summary>
/// Extension methods used to determine what browser a visitor wants to be seen as using.
/// </summary>
public static class BrowserHelpers
{
/// <summary>
/// Stock IE6 user agent string
/// </summary>
private const string DesktopUserAgent = "Mozilla/4.0 (compatible; MSIE 6.1; Windows XP)";
/// <summary>
/// Stock Windows Mobile 6.0 user agent string
/// </summary>
private const string MobileUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 8.12; MSIEMobile 6.0)";
private static readonly object _browserOverrideKey = new object();
private static readonly object _userAgentKey = new object();
/// <summary>
/// Clears the set browser for the request. After clearing the browser the overridden browser will be the browser for the request.
/// </summary>
public static void ClearOverriddenBrowser(this HttpContextBase httpContext)
{
SetOverriddenBrowser(httpContext, userAgent: null);
}
// Default implementation to generate an HttpBrowserCapabilities object using the current HttpCapabilitiesProvider
private static HttpBrowserCapabilitiesBase CreateOverriddenBrowser(string userAgent)
{
HttpBrowserCapabilities overriddenBrowser = new HttpContext(new UserAgentWorkerRequest(userAgent)).Request.Browser;
return new HttpBrowserCapabilitiesWrapper(overriddenBrowser);
}
/// <summary>
/// Gets the overridden browser for the request based on the overridden user agent.
/// If no overridden user agent is set, returns the browser for the request.
/// </summary>
public static HttpBrowserCapabilitiesBase GetOverriddenBrowser(this HttpContextBase httpContext)
{
return GetOverriddenBrowser(httpContext, CreateOverriddenBrowser);
}
internal static HttpBrowserCapabilitiesBase GetOverriddenBrowser(this HttpContextBase httpContext, Func<string, HttpBrowserCapabilitiesBase> createBrowser)
{
HttpBrowserCapabilitiesBase overriddenBrowser = (HttpBrowserCapabilitiesBase)httpContext.Items[_browserOverrideKey];
if (overriddenBrowser == null)
{
string overriddenUserAgent = GetOverriddenUserAgent(httpContext);
if (!String.Equals(overriddenUserAgent, httpContext.Request.UserAgent))
{
overriddenBrowser = createBrowser(overriddenUserAgent);
}
else
{
overriddenBrowser = httpContext.Request.Browser;
}
httpContext.Items[_browserOverrideKey] = overriddenBrowser;
}
return overriddenBrowser;
}
/// <summary>
/// Gets the overridden user agent for the request. If no overridden user agent is set, returns the user agent for the request.
/// </summary>
public static string GetOverriddenUserAgent(this HttpContextBase httpContext)
{
return (string)httpContext.Items[_userAgentKey] ??
BrowserOverrideStores.Current.GetOverriddenUserAgent(httpContext) ??
httpContext.Request.UserAgent;
}
/// <summary>
/// Gets a string that varies based upon the type of the browser. Can be used to override
/// System.Web.HttpApplication.GetVaryByCustomString to differentiate cache keys based on
/// the overridden browser.
/// </summary>
public static string GetVaryByCustomStringForOverriddenBrowser(this HttpContext httpContext)
{
return GetVaryByCustomStringForOverriddenBrowser(new HttpContextWrapper(httpContext));
}
/// <summary>
/// Gets a string that varies based upon the type of the browser. Can be used to override
/// System.Web.HttpApplication.GetVaryByCustomString to differentiate cache keys based on
/// the overridden browser.
/// </summary>
public static string GetVaryByCustomStringForOverriddenBrowser(this HttpContextBase httpContext)
{
return GetVaryByCustomStringForOverriddenBrowser(httpContext, userAgent => CreateOverriddenBrowser(userAgent));
}
internal static string GetVaryByCustomStringForOverriddenBrowser(this HttpContextBase httpContext, Func<string, HttpBrowserCapabilitiesBase> generateBrowser)
{
return GetOverriddenBrowser(httpContext, generateBrowser).Type;
}
/// <summary>
/// Sets the overridden user agent for the request using a BrowserOverride.
/// </summary>
public static void SetOverriddenBrowser(this HttpContextBase httpContext, BrowserOverride browserOverride)
{
string userAgent = null;
switch (browserOverride)
{
case BrowserOverride.Desktop:
// bug:262389 override only if the request was not made from a browser or the browser is not of a desktop device
if (httpContext.Request.Browser == null || httpContext.Request.Browser.IsMobileDevice)
{
userAgent = DesktopUserAgent;
}
break;
case BrowserOverride.Mobile:
if (httpContext.Request.Browser == null || !httpContext.Request.Browser.IsMobileDevice)
{
userAgent = MobileUserAgent;
}
break;
}
if (userAgent != null)
{
SetOverriddenBrowser(httpContext, userAgent);
}
else
{
ClearOverriddenBrowser(httpContext);
}
}
/// <summary>
/// Sets the overridden user agent for the request using a string
/// </summary>
public static void SetOverriddenBrowser(this HttpContextBase httpContext, string userAgent)
{
// Set the overridden user agent and clear the overridden browser
// so that it can be generated from the new overridden user agent.
httpContext.Items[_userAgentKey] = userAgent;
httpContext.Items[_browserOverrideKey] = null;
BrowserOverrideStores.Current.SetOverriddenUserAgent(httpContext, userAgent);
}
private sealed class UserAgentWorkerRequest : SimpleWorkerRequest
{
private readonly string _userAgent;
public UserAgentWorkerRequest(string userAgent)
: base(String.Empty, String.Empty, output: null)
{
_userAgent = userAgent;
}
public override string GetKnownRequestHeader(int index)
{
return index == HeaderUserAgent ? _userAgent : null;
}
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.WebPages
{
/// <summary>
/// BrowserOverrides can be used by BrowserHelpers to override the browser for a particular request.
/// </summary>
public enum BrowserOverride
{
Desktop,
Mobile
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.WebPages
{
/// <summary>
/// The current BrowserOverrideStore is used to get and set the user agent of a request.
/// For an example see CookieBasedBrowserOverrideStore.
/// </summary>
public abstract class BrowserOverrideStore
{
public abstract string GetOverriddenUserAgent(HttpContextBase httpContext);
public abstract void SetOverriddenUserAgent(HttpContextBase httpContext, string userAgent);
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.WebPages
{
public class BrowserOverrideStores
{
private static BrowserOverrideStores _instance = new BrowserOverrideStores();
private BrowserOverrideStore _currentOverrideStore = new CookieBrowserOverrideStore();
/// <summary>
/// The current BrowserOverrideStore
/// </summary>
public static BrowserOverrideStore Current
{
get { return _instance.CurrentInternal; }
set { _instance.CurrentInternal = value; }
}
internal BrowserOverrideStore CurrentInternal
{
get { return _currentOverrideStore; }
set { _currentOverrideStore = value ?? new RequestBrowserOverrideStore(); }
}
}
}

View File

@ -0,0 +1,204 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.Caching;
using System.Web.Compilation;
using System.Web.Hosting;
using System.Web.Util;
using System.Xml.Linq;
using Microsoft.Internal.Web.Utils;
namespace System.Web.WebPages
{
/// <summary>
/// Wraps the caching and instantiation of paths of the BuildManager.
/// In case of precompiled non-updateable sites, the only way to verify if a file exists is to call BuildManager.GetObjectFactory. However this method is less performant than
/// VirtualPathProvider.FileExists which is used for all other scenarios. In this class, we optimize for the first scenario by storing the results of GetObjectFactory for a
/// long duration.
/// </summary>
internal sealed class BuildManagerWrapper : IVirtualPathFactory
{
internal static readonly Guid KeyGuid = Guid.NewGuid();
private static readonly TimeSpan _objectFactoryCacheDuration = TimeSpan.FromMinutes(1);
private readonly IVirtualPathUtility _virtualPathUtility;
private readonly VirtualPathProvider _vpp;
private readonly bool _isPrecompiled;
private readonly FileExistenceCache _vppCache;
private IEnumerable<string> _supportedExtensions;
public BuildManagerWrapper()
: this(HostingEnvironment.VirtualPathProvider, new VirtualPathUtilityWrapper())
{
}
public BuildManagerWrapper(VirtualPathProvider vpp, IVirtualPathUtility virtualPathUtility)
{
_vpp = vpp;
_virtualPathUtility = virtualPathUtility;
_isPrecompiled = IsNonUpdatablePrecompiledApp();
if (!_isPrecompiled)
{
_vppCache = new FileExistenceCache(vpp);
}
}
public IEnumerable<string> SupportedExtensions
{
get { return _supportedExtensions ?? WebPageHttpHandler.GetRegisteredExtensions(); }
set { _supportedExtensions = value; }
}
/// <summary>
/// Determines if a page exists in the website.
/// This method switches between a long duration cache or a short duration FileExistenceCache depending on whether the site is precompiled.
/// This is an optimization because BuildManager.GetObjectFactory is comparably slower than performing VirtualPathFactory.Exists
/// </summary>
public bool Exists(string virtualPath)
{
if (_isPrecompiled)
{
return ExistsInPrecompiledSite(virtualPath);
}
return ExistsInVpp(virtualPath);
}
/// <summary>
/// An app's is precompiled for our purposes if
/// (a) it has a PreCompiledApp.config file in the site root,
/// (b) The PreCompiledApp.config says that the app is not Updatable.
/// </summary>
/// <remarks>
/// This code is based on System.Web.DynamicData.Misc.IsNonUpdatablePrecompiledAppNoCache (DynamicData)
/// </remarks>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "We want to replicate the behavior of BuildManager which catches all exceptions.")]
internal bool IsNonUpdatablePrecompiledApp()
{
if (_vpp == null)
{
return false;
}
var virtualPath = _virtualPathUtility.ToAbsolute("~/PrecompiledApp.config");
if (!_vpp.FileExists(virtualPath))
{
return false;
}
XDocument document;
using (var stream = _vpp.GetFile(virtualPath).Open())
{
try
{
document = XDocument.Load(_vpp.GetFile(virtualPath).Open());
}
catch
{
// If we are unable to load the file, ignore it. The BuildManager behaves identically.
return false;
}
}
if (document.Root == null || !document.Root.Name.LocalName.Equals("precompiledApp", StringComparison.OrdinalIgnoreCase))
{
return false;
}
var updatableAttribute = document.Root.Attribute("updatable");
if (updatableAttribute != null)
{
bool result;
return Boolean.TryParse(updatableAttribute.Value, out result) && (result == false);
}
return false;
}
private bool ExistsInPrecompiledSite(string virtualPath)
{
var key = GetKeyFromVirtualPath(virtualPath);
// We assume that the key is unique enough to avoid collisions.
var buildManagerResult = (BuildManagerResult)HttpRuntime.Cache.Get(key);
if (buildManagerResult == null)
{
// For precompiled apps, we cache the ObjectFactory and use it in the CreateInstance method.
var objectFactory = GetObjectFactory(virtualPath);
buildManagerResult = new BuildManagerResult { ObjectFactory = objectFactory, Exists = objectFactory != null };
// Cache the result with a sliding expiration for a long duration.
HttpRuntime.Cache.Add(key, buildManagerResult, null, Cache.NoAbsoluteExpiration, _objectFactoryCacheDuration, CacheItemPriority.Low, null);
}
return buildManagerResult.Exists;
}
/// <summary>
/// Determines if a site exists in the VirtualPathProvider.
/// Results of hits are cached for a very short amount of time in the FileExistenceCache.
/// </summary>
private bool ExistsInVpp(string virtualPath)
{
Debug.Assert(_vppCache != null);
return _vppCache.FileExists(virtualPath);
}
/// <summary>
/// Determines if an ObjectFactory exists for the virtualPath.
/// The BuildManager complains if we pass in extensions that aren't registered for compilation. So we ensure that the virtual path is not
/// extensionless and that it is one of the extension
/// </summary>
private IWebObjectFactory GetObjectFactory(string virtualPath)
{
if (IsPathExtensionSupported(virtualPath))
{
return BuildManager.GetObjectFactory(virtualPath, throwIfNotFound: false);
}
return null;
}
public object CreateInstance(string virtualPath)
{
return CreateInstanceOfType<object>(virtualPath);
}
public T CreateInstanceOfType<T>(string virtualPath) where T : class
{
if (_isPrecompiled)
{
var buildManagerResult = (BuildManagerResult)HttpRuntime.Cache.Get(GetKeyFromVirtualPath(virtualPath));
// The cache could have evicted our results. In this case, we'll simply fall through to CreateInstanceFromVirtualPath
if (buildManagerResult != null)
{
Debug.Assert(buildManagerResult.Exists && buildManagerResult.ObjectFactory != null, "This method must only be called if the file exists.");
return buildManagerResult.ObjectFactory.CreateInstance() as T;
}
}
return (T)BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(T));
}
/// <summary>
/// Determines if the extension is one of the extensions registered with WebPageHttpHandler.
/// </summary>
public bool IsPathExtensionSupported(string virtualPath)
{
string extension = PathUtil.GetExtension(virtualPath);
return !String.IsNullOrEmpty(extension)
&& SupportedExtensions.Contains(extension.Substring(1), StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// Creates a reasonably unique key for a given virtual path by concatenating it with a Guid.
/// </summary>
private static string GetKeyFromVirtualPath(string virtualPath)
{
return KeyGuid.ToString() + "_" + virtualPath;
}
private class BuildManagerResult
{
public bool Exists { get; set; }
public IWebObjectFactory ObjectFactory { get; set; }
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.WebPages
{
internal class DisposableAction : IDisposable
{
private Action _action;
private bool _hasDisposed;
public DisposableAction(Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
_action = action;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// If we were disposed by the finalizer it's because the user didn't use a "using" block, so don't do anything!
if (disposing)
{
lock (this)
{
if (!_hasDisposed)
{
_hasDisposed = true;
_action();
}
}
}
}
}
}

View File

@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.WebPages
{
/// <summary>
/// The default BrowserOverrideStore. Gets overridden user agent for a request from a cookie.
/// Creates a cookie to set the overridden user agent.
/// </summary>
public class CookieBrowserOverrideStore : BrowserOverrideStore
{
internal static readonly string BrowserOverrideCookieName = ".ASPXBrowserOverride";
private readonly int _daysToExpire;
/// <summary>
/// Creates the BrowserOverrideStore setting any browser override cookie to expire in 7 days.
/// </summary>
public CookieBrowserOverrideStore()
: this(daysToExpire: 7)
{
}
/// <summary>
/// Constructor to control the expiration of the browser override cookie.
/// </summary>
public CookieBrowserOverrideStore(int daysToExpire)
{
_daysToExpire = daysToExpire;
}
/// <summary>
/// Looks for a user agent by searching for the browser override cookie. If no cookie is found
/// returns null.
/// </summary>
public override string GetOverriddenUserAgent(HttpContextBase httpContext)
{
// Check the response to see if the cookie has been set somewhere in the current request.
HttpCookieCollection responseCookies = httpContext.Response.Cookies;
// NOTE: Only look for the key (via AllKeys) so a new cookie is not automatically created.
string[] cookieNames = responseCookies.AllKeys;
// NOTE: use a simple for loop since it performs an order of magnitude faster than .Contains()
// and this is a hot path that gets executed for every request.
for (int i = 0; i < cookieNames.Length; i++)
{
// HttpCookieCollection uses OrdinalIgnoreCase comparison for its keys
if (String.Equals(cookieNames[i], BrowserOverrideCookieName, StringComparison.OrdinalIgnoreCase))
{
HttpCookie currentOverriddenBrowserCookie = responseCookies[BrowserOverrideCookieName];
if (currentOverriddenBrowserCookie.Value != null)
{
return currentOverriddenBrowserCookie.Value;
}
else
{
// The cookie has no value. It was cleared on the current request so return null.
return null;
}
}
}
// If there was no cookie found in the response check the request.
var requestOverrideCookie = httpContext.Request.Cookies[BrowserOverrideCookieName];
if (requestOverrideCookie != null)
{
return requestOverrideCookie.Value;
}
return null;
}
/// <summary>
/// Adds a browser override cookie with the set user agent to the response of the current request.
/// If the user agent is null the browser override cookie is set to expire, otherwise its expiration is set
/// to daysToExpire, specified when CookieBasedOverrideStore is created.
/// </summary>
public override void SetOverriddenUserAgent(HttpContextBase httpContext, string userAgent)
{
HttpCookie browserOverrideCookie = new HttpCookie(BrowserOverrideCookieName, HttpUtility.UrlEncode(userAgent));
if (userAgent == null)
{
browserOverrideCookie.Expires = DateTime.Now.AddDays(-1);
}
else
{
// Only set expiration if the cookie should live longer than the current session
if (_daysToExpire > 0)
{
browserOverrideCookie.Expires = DateTime.Now.AddDays(_daysToExpire);
}
}
httpContext.Response.Cookies.Remove(BrowserOverrideCookieName);
httpContext.Response.Cookies.Add(browserOverrideCookie);
}
}
}

View File

@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.IO;
namespace System.Web.WebPages
{
/// <summary>
/// The <see cref="DefaultDisplayMode"/> can take any suffix and determine if there is a corresponding
/// file that exists given a path and request by transforming the path to contain the suffix.
/// Add a new DefaultDisplayMode to the Modes collection to handle a new suffix or inherit from
/// DefaultDisplayMode to provide custom logic to transform paths with a suffix.
/// </summary>
public class DefaultDisplayMode : IDisplayMode
{
private readonly string _suffix;
public DefaultDisplayMode()
: this(DisplayModeProvider.DefaultDisplayModeId)
{
}
public DefaultDisplayMode(string suffix)
{
_suffix = suffix ?? String.Empty;
}
/// <summary>
/// When set, the <see cref="DefaultDisplayMode"/> will only be available to return Display Info for a request
/// if the ContextCondition evaluates to true.
/// </summary>
public Func<HttpContextBase, bool> ContextCondition { get; set; }
public virtual string DisplayModeId
{
get { return _suffix; }
}
public bool CanHandleContext(HttpContextBase httpContext)
{
return ContextCondition == null || ContextCondition(httpContext);
}
/// <summary>
/// Returns DisplayInfo with the transformed path if it exists.
/// </summary>
public virtual DisplayInfo GetDisplayInfo(HttpContextBase httpContext, string virtualPath, Func<string, bool> virtualPathExists)
{
string transformedFilename = TransformPath(virtualPath, _suffix);
if (transformedFilename != null && virtualPathExists(transformedFilename))
{
return new DisplayInfo(transformedFilename, this);
}
return null;
}
/// <summary>
/// Transforms paths according to the following rules:
/// \some\path.blah\file.txt.zip -> \some\path.blah\file.txt.suffix.zip
/// \some\path.blah\file -> \some\path.blah\file.suffix
/// </summary>
protected virtual string TransformPath(string virtualPath, string suffix)
{
if (String.IsNullOrEmpty(suffix))
{
return virtualPath;
}
string extension = Path.GetExtension(virtualPath);
return Path.ChangeExtension(virtualPath, suffix + extension);
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.WebPages
{
/// <summary>
/// DisplayInfo wraps the resolved file path and IDisplayMode for a request and path.
/// The returned IDisplayMode can be used to resolve other page elements for the request.
/// </summary>
public class DisplayInfo
{
public DisplayInfo(string filePath, IDisplayMode displayMode)
{
if (filePath == null)
{
throw new ArgumentNullException("filePath");
}
if (displayMode == null)
{
throw new ArgumentNullException("displayMode");
}
FilePath = filePath;
DisplayMode = displayMode;
}
/// <summary>
/// The Display Mode used to resolve a virtual path.
/// </summary>
public IDisplayMode DisplayMode { get; private set; }
/// <summary>
/// Resolved path of a file that exists.
/// </summary>
public string FilePath { get; private set; }
}
}

View File

@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
namespace System.Web.WebPages
{
public sealed class DisplayModeProvider
{
public static readonly string MobileDisplayModeId = "Mobile";
public static readonly string DefaultDisplayModeId = String.Empty;
private static readonly object _displayModeKey = new object();
private static readonly DisplayModeProvider _instance = new DisplayModeProvider();
private readonly List<IDisplayMode> _displayModes = new List<IDisplayMode>
{
new DefaultDisplayMode(MobileDisplayModeId)
{
ContextCondition = context => context.GetOverriddenBrowser().IsMobileDevice
},
new DefaultDisplayMode()
};
internal DisplayModeProvider()
{
// The type is a psuedo-singleton. A user would gain nothing from constructing it since we won't use anything but DisplayModeProvider.Instance internally.
}
/// <summary>
/// Restricts the search for Display Info to Display Modes either equal to or following the current
/// Display Mode in Modes. For example, a page being rendered in the Default Display Mode will not
/// display Mobile partial views in order to achieve a consistent look and feel.
/// </summary>
public bool RequireConsistentDisplayMode { get; set; }
public static DisplayModeProvider Instance
{
get { return _instance; }
}
/// <summary>
/// All Display Modes that are available to handle a request.
/// </summary>
public IList<IDisplayMode> Modes
{
get { return _displayModes; }
}
/// <summary>
/// Returns any IDisplayMode that can handle the given request.
/// </summary>
public IEnumerable<IDisplayMode> GetAvailableDisplayModesForContext(HttpContextBase httpContext, IDisplayMode currentDisplayMode)
{
return GetAvailableDisplayModesForContext(httpContext, currentDisplayMode, RequireConsistentDisplayMode).ToList();
}
internal IEnumerable<IDisplayMode> GetAvailableDisplayModesForContext(HttpContextBase httpContext, IDisplayMode currentDisplayMode, bool requireConsistentDisplayMode)
{
IEnumerable<IDisplayMode> possibleDisplayModes = (requireConsistentDisplayMode && currentDisplayMode != null) ? Modes.SkipWhile(mode => mode != currentDisplayMode) : Modes;
return possibleDisplayModes.Where(mode => mode.CanHandleContext(httpContext));
}
/// <summary>
/// Returns DisplayInfo from the first IDisplayMode in Modes that can handle the given request and locate the virtual path.
/// If currentDisplayMode is not null and RequireConsistentDisplayMode is set to true the search for DisplayInfo will only
/// start with the currentDisplayMode.
/// </summary>
public DisplayInfo GetDisplayInfoForVirtualPath(string virtualPath, HttpContextBase httpContext, Func<string, bool> virtualPathExists, IDisplayMode currentDisplayMode)
{
return GetDisplayInfoForVirtualPath(virtualPath, httpContext, virtualPathExists, currentDisplayMode, RequireConsistentDisplayMode);
}
internal DisplayInfo GetDisplayInfoForVirtualPath(string virtualPath, HttpContextBase httpContext, Func<string, bool> virtualPathExists, IDisplayMode currentDisplayMode,
bool requireConsistentDisplayMode)
{
IEnumerable<IDisplayMode> possibleDisplayModes = GetAvailableDisplayModesForContext(httpContext, currentDisplayMode, requireConsistentDisplayMode);
return possibleDisplayModes.Select(mode => mode.GetDisplayInfo(httpContext, virtualPath, virtualPathExists))
.FirstOrDefault(info => info != null);
}
internal static IDisplayMode GetDisplayMode(HttpContextBase context)
{
return context != null ? context.Items[_displayModeKey] as IDisplayMode : null;
}
internal static void SetDisplayMode(HttpContextBase context, IDisplayMode displayMode)
{
if (context != null)
{
context.Items[_displayModeKey] = displayMode;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More