diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs
index 0e3f5adce0..d278980cd9 100644
--- a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs
@@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
/// The path of the page to locate inherited chunks for.
/// A of parsed _GlobalImport
/// s.
- public IReadOnlyList GetInheritedCodeTrees([NotNull] string pagePath)
+ public virtual IReadOnlyList GetInheritedCodeTrees([NotNull] string pagePath)
{
var inheritedCodeTrees = new List();
var templateEngine = new RazorTemplateEngine(_razorHost);
diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Internal/DesignTimeRazorPathNormalizer.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Internal/DesignTimeRazorPathNormalizer.cs
new file mode 100644
index 0000000000..ac433aaee6
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Internal/DesignTimeRazorPathNormalizer.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using Microsoft.Framework.Internal;
+
+namespace Microsoft.AspNet.Mvc.Razor.Internal
+{
+ public class DesignTimeRazorPathNormalizer : RazorPathNormalizer
+ {
+ private readonly string _applicationRoot;
+
+ public DesignTimeRazorPathNormalizer([NotNull] string applicationRoot)
+ {
+ _applicationRoot = applicationRoot;
+ }
+
+ public override string NormalizePath([NotNull] string path)
+ {
+ // Need to convert path to application relative (rooted paths are passed in during design time).
+ if (Path.IsPathRooted(path) && path.StartsWith(_applicationRoot, StringComparison.Ordinal))
+ {
+ path = path.Substring(_applicationRoot.Length);
+ }
+
+ return path;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Internal/RazorPathNormalizer.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Internal/RazorPathNormalizer.cs
new file mode 100644
index 0000000000..f459534fb3
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Internal/RazorPathNormalizer.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.Framework.Internal;
+
+namespace Microsoft.AspNet.Mvc.Razor.Internal
+{
+ public class RazorPathNormalizer
+ {
+ public virtual string NormalizePath([NotNull] string path)
+ {
+ return path;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs
index 86fa368baa..7c33a5015a 100644
--- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs
+++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Mvc.Razor.Directives;
+using Microsoft.AspNet.Mvc.Razor.Internal;
using Microsoft.AspNet.Razor;
using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Generator.Compiler;
@@ -36,31 +37,13 @@ namespace Microsoft.AspNet.Mvc.Razor
// This field holds the type name without the generic decoration (MyBaseType)
private readonly string _baseType;
private readonly ICodeTreeCache _codeTreeCache;
+ private readonly RazorPathNormalizer _pathNormalizer;
private ChunkInheritanceUtility _chunkInheritanceUtility;
-#if NET45
- ///
- /// Initializes a new instance of with the specified
- /// .
- ///
- /// The path to the application base.
- // Note: This constructor is used by tooling and is created once for each
- // Razor page that is loaded. Consequently, each loaded page has its own copy of
- // the CodeTreeCache, but this ok - having a shared CodeTreeCache per application in tooling
- // is problematic to manage.
- public MvcRazorHost(string root) :
- this(new DefaultCodeTreeCache(new PhysicalFileProvider(root)))
- {
- ApplicationRoot = root;
- }
-#endif
- ///
- /// Initializes a new instance of using the specified .
- ///
- /// A rooted at the application base path.
- public MvcRazorHost(ICodeTreeCache codeTreeCache)
+ internal MvcRazorHost(ICodeTreeCache codeTreeCache, RazorPathNormalizer pathNormalizer)
: base(new CSharpRazorCodeLanguage())
{
+ _pathNormalizer = pathNormalizer;
_baseType = BaseType;
_codeTreeCache = codeTreeCache;
@@ -115,10 +98,26 @@ namespace Microsoft.AspNet.Mvc.Razor
#if NET45
///
- /// The path to the application root.
+ /// Initializes a new instance of with the specified .
///
- public string ApplicationRoot { get; }
+ /// The path to the application base.
+ // Note: This constructor is used by tooling and is created once for each
+ // Razor page that is loaded. Consequently, each loaded page has its own copy of
+ // the CodeTreeCache, but this ok - having a shared CodeTreeCache per application in tooling
+ // is problematic to manage.
+ public MvcRazorHost(string root)
+ : this(new DefaultCodeTreeCache(new PhysicalFileProvider(root)), new DesignTimeRazorPathNormalizer(root))
+ {
+ }
#endif
+ ///
+ /// Initializes a new instance of using the specified .
+ ///
+ /// An rooted at the application base path.
+ public MvcRazorHost(ICodeTreeCache codeTreeCache)
+ : this(codeTreeCache, new RazorPathNormalizer())
+ {
+ }
///
/// Gets the model type used by default when no model is specified.
@@ -168,7 +167,8 @@ namespace Microsoft.AspNet.Mvc.Razor
get { return "CreateModelExpression"; }
}
- private ChunkInheritanceUtility ChunkInheritanceUtility
+ // Internal for testing
+ internal ChunkInheritanceUtility ChunkInheritanceUtility
{
get
{
@@ -180,6 +180,10 @@ namespace Microsoft.AspNet.Mvc.Razor
return _chunkInheritanceUtility;
}
+ set
+ {
+ _chunkInheritanceUtility = value;
+ }
}
///
@@ -194,13 +198,7 @@ namespace Microsoft.AspNet.Mvc.Razor
///
public override RazorParser DecorateRazorParser([NotNull] RazorParser razorParser, string sourceFileName)
{
-#if NET45
- // Need to convert sourceFileName to application relative (rooted paths are passed in by tooling).
- if (Path.IsPathRooted(sourceFileName))
- {
- sourceFileName = sourceFileName.Substring(ApplicationRoot.Length);
- }
-#endif
+ sourceFileName = _pathNormalizer.NormalizePath(sourceFileName);
var inheritedCodeTrees = ChunkInheritanceUtility.GetInheritedCodeTrees(sourceFileName);
return new MvcRazorParser(razorParser, inheritedCodeTrees, DefaultInheritedChunks);
diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorHostTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorHostTest.cs
index ffef6acf74..ffb2a8a9d0 100644
--- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorHostTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorHostTest.cs
@@ -1,20 +1,52 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNet.Mvc.Razor.Directives;
+using Microsoft.AspNet.Mvc.Razor.Internal;
using Microsoft.AspNet.Razor;
using Microsoft.AspNet.Razor.Generator;
using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Generator.Compiler.CSharp;
+using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Text;
+using Microsoft.Framework.Internal;
using Xunit;
namespace Microsoft.AspNet.Mvc.Razor
{
public class MvcRazorHostTest
{
+ [Theory]
+ [InlineData("//")]
+ [InlineData("C:/")]
+ [InlineData(@"\\")]
+ [InlineData(@"C:\")]
+ public void DecorateRazorParser_DesignTimeRazorPathNormalizer_NormalizesChunkInheritanceUtilityPaths(
+ string rootPrefix)
+ {
+ // Arrange
+ var rootedAppPath = $"{rootPrefix}SomeComputer/Location/Project/";
+ var rootedFilePath = $"{rootPrefix}SomeComputer/Location/Project/src/file.cshtml";
+ var host = new MvcRazorHost(
+ codeTreeCache: null,
+ pathNormalizer: new DesignTimeRazorPathNormalizer(rootedAppPath));
+ var parser = new RazorParser(
+ host.CodeLanguage.CreateCodeParser(),
+ host.CreateMarkupParser(),
+ tagHelperDescriptorResolver: null);
+ var chunkInheritanceUtility = new PathValidatingChunkInheritanceUtility(host);
+ host.ChunkInheritanceUtility = chunkInheritanceUtility;
+
+ // Act
+ host.DecorateRazorParser(parser, rootedFilePath);
+
+ // Assert
+ Assert.Equal("src/file.cshtml", chunkInheritanceUtility.InheritedCodeTreePagePath, StringComparer.Ordinal);
+ }
+
[Fact]
public void MvcRazorHost_EnablesInstrumentationByDefault()
{
@@ -254,6 +286,23 @@ namespace Microsoft.AspNet.Mvc.Razor
generatedLocation: new MappingLocation(generatedLocation, contentLength));
}
+ private class PathValidatingChunkInheritanceUtility : ChunkInheritanceUtility
+ {
+ public PathValidatingChunkInheritanceUtility(MvcRazorHost razorHost)
+ : base(razorHost, codeTreeCache: null, defaultInheritedChunks: new Chunk[0])
+ {
+ }
+
+ public string InheritedCodeTreePagePath { get; private set; }
+
+ public override IReadOnlyList GetInheritedCodeTrees([NotNull] string pagePath)
+ {
+ InheritedCodeTreePagePath = pagePath;
+
+ return new CodeTree[0];
+ }
+ }
+
///
/// Used when testing Tag Helpers, it disables the unique ID generation feature.
///