diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs index df6e13d3a..1c0c187ee 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; @@ -52,6 +53,12 @@ public static IEnumerable CreateDescriptors( [NotNull] ErrorSink errorSink) { var typeInfo = type.GetTypeInfo(); + + if (ShouldSkipDescriptorCreation(designTime, typeInfo)) + { + return Enumerable.Empty(); + } + var attributeDescriptors = GetAttributeDescriptors(type, designTime, errorSink); var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink); @@ -264,6 +271,11 @@ private static IEnumerable GetAttributeDescriptors var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty); foreach (var property in accessibleProperties) { + if (ShouldSkipDescriptorCreation(designTime, property)) + { + return Enumerable.Empty(); + } + var attributeNameAttribute = property.GetCustomAttribute(inherit: false); var hasExplicitName = attributeNameAttribute != null && !string.IsNullOrEmpty(attributeNameAttribute.Name); @@ -364,6 +376,18 @@ internal static bool ValidateTagHelperAttributeDescriptor( nameOrPrefix); } + private static bool ShouldSkipDescriptorCreation(bool designTime, MemberInfo memberInfo) + { + if (designTime) + { + var editorBrowsableAttribute = memberInfo.GetCustomAttribute(inherit: false); + + return editorBrowsableAttribute != null && editorBrowsableAttribute.State == EditorBrowsableState.Never; + } + + return false; + } + private static bool ValidateTagHelperAttributeNameOrPrefix( string attributeNameOrPrefix, Type parentType, diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs index b4c2170f1..e34223600 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Reflection; using Microsoft.AspNet.Razor.TagHelpers; @@ -15,6 +16,237 @@ public class TagHelperDescriptorFactoryTest private static readonly string AssemblyName = typeof(TagHelperDescriptorFactoryTest).GetTypeInfo().Assembly.GetName().Name; + public static TheoryData EditorBrowsableData + { + get + { + // tagHelperType, designTime, expectedDescriptors + return new TheoryData + { + { + typeof(InheritedEditorBrowsableTagHelper), + true, + new[] + { + new TagHelperDescriptor( + tagName: "inherited-editor-browsable", + typeName: typeof(InheritedEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "property", + propertyName: nameof(InheritedEditorBrowsableTagHelper.Property), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null) + }) + } + }, + { typeof(EditorBrowsableTagHelper), true, new TagHelperDescriptor[0] }, + { + typeof(EditorBrowsableTagHelper), + false, + new[] + { + new TagHelperDescriptor( + tagName: "editor-browsable", + typeName: typeof(EditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "property", + propertyName: nameof(EditorBrowsableTagHelper.Property), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null) + }) + } + }, + { + typeof(HiddenPropertyEditorBrowsableTagHelper), + true, + new[] + { + new TagHelperDescriptor( + tagName: "hidden-property-editor-browsable", + typeName: typeof(HiddenPropertyEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new TagHelperAttributeDescriptor[0]) + } + }, + { + typeof(HiddenPropertyEditorBrowsableTagHelper), + false, + new[] + { + new TagHelperDescriptor( + tagName: "hidden-property-editor-browsable", + typeName: typeof(HiddenPropertyEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "property", + propertyName: nameof(HiddenPropertyEditorBrowsableTagHelper.Property), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null) + }) + } + }, + { + typeof(OverriddenEditorBrowsableTagHelper), + true, + new[] + { + new TagHelperDescriptor( + tagName: "overridden-editor-browsable", + typeName: typeof(OverriddenEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "property", + propertyName: nameof(OverriddenEditorBrowsableTagHelper.Property), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null) + }) + } + }, + { + typeof(MultiPropertyEditorBrowsableTagHelper), + true, + new[] + { + new TagHelperDescriptor( + tagName: "multi-property-editor-browsable", + typeName: typeof(MultiPropertyEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "property2", + propertyName: nameof(MultiPropertyEditorBrowsableTagHelper.Property2), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null) + }) + } + }, + { + typeof(MultiPropertyEditorBrowsableTagHelper), + false, + new[] + { + new TagHelperDescriptor( + tagName: "multi-property-editor-browsable", + typeName: typeof(MultiPropertyEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "property", + propertyName: nameof(MultiPropertyEditorBrowsableTagHelper.Property), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null), + new TagHelperAttributeDescriptor( + name: "property2", + propertyName: nameof(MultiPropertyEditorBrowsableTagHelper.Property2), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null) + }) + } + }, + { + typeof(OverriddenPropertyEditorBrowsableTagHelper), + true, + new[] + { + new TagHelperDescriptor( + tagName: "overridden-property-editor-browsable", + typeName: typeof(OverriddenPropertyEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new TagHelperAttributeDescriptor[0]) + } + }, + { + typeof(OverriddenPropertyEditorBrowsableTagHelper), + false, + new[] + { + new TagHelperDescriptor( + tagName: "overridden-property-editor-browsable", + typeName: typeof(OverriddenPropertyEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "property", + propertyName: nameof(OverriddenPropertyEditorBrowsableTagHelper.Property), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null), + new TagHelperAttributeDescriptor( + name: "property2", + propertyName: nameof(OverriddenPropertyEditorBrowsableTagHelper.Property2), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null) + }) + } + }, + { + typeof(DefaultEditorBrowsableTagHelper), + true, + new[] + { + new TagHelperDescriptor( + tagName: "default-editor-browsable", + typeName: typeof(DefaultEditorBrowsableTagHelper).FullName, + assemblyName: AssemblyName, + attributes: new[] + { + new TagHelperAttributeDescriptor( + name: "property", + propertyName: nameof(DefaultEditorBrowsableTagHelper.Property), + typeName: typeof(int).FullName, + isIndexer: false, + designTimeDescriptor: null) + }) + } + }, + { typeof(MultiEditorBrowsableTagHelper), true, new TagHelperDescriptor[0] } + }; + } + } + + [Theory] + [MemberData(nameof(EditorBrowsableData))] + public void CreateDescriptors_UnderstandsEditorBrowsableAttribute( + Type tagHelperType, + bool designTime, + TagHelperDescriptor[] expectedDescriptors) + { + // Arrange + var errorSink = new ErrorSink(); + + // Act + var descriptors = TagHelperDescriptorFactory.CreateDescriptors( + AssemblyName, + tagHelperType, + designTime, + errorSink); + + // Assert + Assert.Empty(errorSink.Errors); + Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.Default); + } + public static TheoryData AttributeTargetData { get @@ -1564,6 +1796,59 @@ private static TheoryData GetInvalidNameOrPrefixData( return data; } + [EditorBrowsable(EditorBrowsableState.Always)] + private class DefaultEditorBrowsableTagHelper : TagHelper + { + [EditorBrowsable(EditorBrowsableState.Always)] + public int Property { get; set; } + } + + private class HiddenPropertyEditorBrowsableTagHelper : TagHelper + { + [EditorBrowsable(EditorBrowsableState.Never)] + public int Property { get; set; } + } + + private class MultiPropertyEditorBrowsableTagHelper : TagHelper + { + [EditorBrowsable(EditorBrowsableState.Never)] + public int Property { get; set; } + + public virtual int Property2 { get; set; } + } + + private class OverriddenPropertyEditorBrowsableTagHelper : MultiPropertyEditorBrowsableTagHelper + { + [EditorBrowsable(EditorBrowsableState.Never)] + public override int Property2 { get; set; } + } + + [EditorBrowsable(EditorBrowsableState.Never)] + private class EditorBrowsableTagHelper : TagHelper + { + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual int Property { get; set; } + } + + private class InheritedEditorBrowsableTagHelper : EditorBrowsableTagHelper + { + public override int Property { get; set; } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + private class OverriddenEditorBrowsableTagHelper : EditorBrowsableTagHelper + { + [EditorBrowsable(EditorBrowsableState.Advanced)] + public override int Property { get; set; } + } + + [TargetElement("p")] + [TargetElement("div")] + [EditorBrowsable(EditorBrowsableState.Never)] + private class MultiEditorBrowsableTagHelper : TagHelper + { + } + [TargetElement(Attributes = "class*")] private class AttributeWildcardTargetingTagHelper : TagHelper {