private void AddTagHelperAttributes(IList <TagHelperAttributeNode> attributes, TagHelperBinding tagHelperBinding)
            {
                var descriptors = tagHelperBinding.Descriptors;
                var renderedBoundAttributeNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                foreach (var attribute in attributes)
                {
                    var attributeValueNode    = attribute.Value;
                    var associatedDescriptors = descriptors.Where(descriptor =>
                                                                  descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attribute.Name, attributeDescriptor)));

                    if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name))
                    {
                        if (attributeValueNode == null)
                        {
                            // Minimized attributes are not valid for bound attributes. TagHelperBlockRewriter has already
                            // logged an error if it was a bound attribute; so we can skip.
                            continue;
                        }

                        foreach (var associatedDescriptor in associatedDescriptors)
                        {
                            var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(a =>
                            {
                                return(TagHelperMatchingConventions.CanSatisfyBoundAttribute(attribute.Name, a));
                            });

                            var setTagHelperProperty = new TagHelperPropertyIntermediateNode()
                            {
                                AttributeName      = attribute.Name,
                                BoundAttribute     = associatedAttributeDescriptor,
                                TagHelper          = associatedDescriptor,
                                AttributeStructure = attribute.AttributeStructure,
                                Source             = BuildSourceSpanFromNode(attributeValueNode),
                                IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(attribute.Name, associatedAttributeDescriptor),
                            };

                            _builder.Push(setTagHelperProperty);
                            attributeValueNode.Accept(this);
                            _builder.Pop();
                        }
                    }
                    else
                    {
                        var addHtmlAttribute = new TagHelperHtmlAttributeIntermediateNode()
                        {
                            AttributeName      = attribute.Name,
                            AttributeStructure = attribute.AttributeStructure
                        };

                        _builder.Push(addHtmlAttribute);
                        if (attributeValueNode != null)
                        {
                            attributeValueNode.Accept(this);
                        }
                        _builder.Pop();
                    }
                }
            }
예제 #2
0
            public override void VisitMarkupMinimizedTagHelperAttribute(MarkupMinimizedTagHelperAttributeSyntax node)
            {
                if (!_featureFlags.AllowMinimizedBooleanTagHelperAttributes)
                {
                    // Minimized attributes are not valid for non-boolean bound attributes. TagHelperBlockRewriter
                    // has already logged an error if it was a non-boolean bound attribute; so we can skip.
                    return;
                }

                var element               = node.FirstAncestorOrSelf <MarkupTagHelperElementSyntax>();
                var descriptors           = element.TagHelperInfo.BindingResult.Descriptors;
                var attributeName         = node.Name.GetContent();
                var associatedDescriptors = descriptors.Where(descriptor =>
                                                              descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor)));

                if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName))
                {
                    foreach (var associatedDescriptor in associatedDescriptors)
                    {
                        var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(a =>
                        {
                            return(TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, a));
                        });

                        var expectsBooleanValue = associatedAttributeDescriptor.ExpectsBooleanValue(attributeName);

                        if (!expectsBooleanValue)
                        {
                            // We do not allow minimized non-boolean bound attributes.
                            return;
                        }

                        var setTagHelperProperty = new TagHelperPropertyIntermediateNode()
                        {
                            AttributeName      = attributeName,
                            BoundAttribute     = associatedAttributeDescriptor,
                            TagHelper          = associatedDescriptor,
                            AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
                            Source             = null,
                            IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(attributeName, associatedAttributeDescriptor),
                        };

                        _builder.Add(setTagHelperProperty);
                    }
                }
                else
                {
                    var addHtmlAttribute = new TagHelperHtmlAttributeIntermediateNode()
                    {
                        AttributeName      = attributeName,
                        AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure
                    };

                    _builder.Add(addHtmlAttribute);
                }
            }
        internal static bool ExpectsBooleanValue(this BoundAttributeDescriptor attribute, string name)
        {
            if (attribute.IsBooleanProperty)
            {
                return(true);
            }

            var isIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, attribute);

            return(isIndexerNameMatch && attribute.IsIndexerBooleanProperty);
        }
예제 #4
0
            public override void VisitMarkupTagHelperAttribute(MarkupTagHelperAttributeSyntax node)
            {
                var element               = node.FirstAncestorOrSelf <MarkupTagHelperElementSyntax>();
                var descriptors           = element.TagHelperInfo.BindingResult.Descriptors;
                var attributeName         = node.Name.GetContent();
                var attributeValueNode    = node.Value;
                var associatedDescriptors = descriptors.Where(descriptor =>
                                                              descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor)));

                if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName))
                {
                    foreach (var associatedDescriptor in associatedDescriptors)
                    {
                        var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(a =>
                        {
                            return(TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, a));
                        });

                        var setTagHelperProperty = new TagHelperPropertyIntermediateNode()
                        {
                            AttributeName      = attributeName,
                            BoundAttribute     = associatedAttributeDescriptor,
                            TagHelper          = associatedDescriptor,
                            AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
                            Source             = BuildSourceSpanFromNode(attributeValueNode),
                            IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(attributeName, associatedAttributeDescriptor),
                        };

                        _builder.Push(setTagHelperProperty);
                        VisitAttributeValue(attributeValueNode);
                        _builder.Pop();
                    }
                }
                else
                {
                    var addHtmlAttribute = new TagHelperHtmlAttributeIntermediateNode()
                    {
                        AttributeName      = attributeName,
                        AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure
                    };

                    _builder.Push(addHtmlAttribute);
                    VisitAttributeValue(attributeValueNode);
                    _builder.Pop();
                }
            }
        public void Matches_ReturnsExpectedResult(
            Action <RequiredAttributeDescriptorBuilder> configure,
            string attributeName,
            string attributeValue,
            bool expectedResult)
        {
            // Arrange

            var builder = new DefaultRequiredAttributeDescriptorBuilder();

            configure(builder);

            var requiredAttibute = builder.Build();

            // Act
            var result = TagHelperMatchingConventions.SatisfiesRequiredAttribute(attributeName, attributeValue, requiredAttibute);

            // Assert
            Assert.Equal(expectedResult, result);
        }
예제 #6
0
        /// <summary>
        /// Gets all tag helpers that match the given HTML tag criteria.
        /// </summary>
        /// <param name="tagName">The name of the HTML tag to match. Providing a '*' tag name
        /// retrieves catch-all <see cref="TagHelperDescriptor"/>s (descriptors that target every tag).</param>
        /// <param name="attributes">Attributes on the HTML tag.</param>
        /// <param name="parentTagName">The parent tag name of the given <paramref name="tagName"/> tag.</param>
        /// <param name="parentIsTagHelper">Is the parent tag of the given <paramref name="tagName"/> tag a tag helper.</param>
        /// <returns><see cref="TagHelperDescriptor"/>s that apply to the given HTML tag criteria.
        /// Will return <c>null</c> if no <see cref="TagHelperDescriptor"/>s are a match.</returns>
        public TagHelperBinding GetBinding(
            string tagName,
            IReadOnlyList <KeyValuePair <string, string> > attributes,
            string parentTagName,
            bool parentIsTagHelper)
        {
            if (!string.IsNullOrEmpty(_tagHelperPrefix) &&
                (tagName.Length <= _tagHelperPrefix.Length ||
                 !tagName.StartsWith(_tagHelperPrefix, StringComparison.OrdinalIgnoreCase)))
            {
                // The tagName doesn't have the tag helper prefix, we can short circuit.
                return(null);
            }

            IEnumerable <TagHelperDescriptor> descriptors;

            // Ensure there's a HashSet to use.
            if (!_registrations.TryGetValue(TagHelperMatchingConventions.ElementCatchAllName, out HashSet <TagHelperDescriptor> catchAllDescriptors))
            {
                descriptors = new HashSet <TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
            }
            else
            {
                descriptors = catchAllDescriptors;
            }

            // If we have a tag name associated with the requested name, we need to combine matchingDescriptors
            // with all the catch-all descriptors.
            if (_registrations.TryGetValue(tagName, out HashSet <TagHelperDescriptor> matchingDescriptors))
            {
                descriptors = matchingDescriptors.Concat(descriptors);
            }

            var           tagNameWithoutPrefix       = _tagHelperPrefix != null ? new StringSegment(tagName, _tagHelperPrefix.Length) : tagName;
            StringSegment parentTagNameWithoutPrefix = parentTagName;

            if (_tagHelperPrefix != null && parentIsTagHelper)
            {
                parentTagNameWithoutPrefix = new StringSegment(parentTagName, _tagHelperPrefix.Length);
            }

            Dictionary <TagHelperDescriptor, IReadOnlyList <TagMatchingRuleDescriptor> > applicableDescriptorMappings = null;

            foreach (var descriptor in descriptors)
            {
                // We're avoiding desccriptor.TagMatchingRules.Where and applicableRules.Any() to avoid
                // Enumerator allocations on this hotpath
                List <TagMatchingRuleDescriptor> applicableRules = null;
                for (var i = 0; i < descriptor.TagMatchingRules.Count; i++)
                {
                    var rule = descriptor.TagMatchingRules[i];
                    if (TagHelperMatchingConventions.SatisfiesRule(tagNameWithoutPrefix, parentTagNameWithoutPrefix, attributes, rule))
                    {
                        if (applicableRules is null)
                        {
                            applicableRules = new List <TagMatchingRuleDescriptor>();
                        }

                        applicableRules.Add(rule);
                    }
                }

                if (applicableRules != null && applicableRules.Count > 0)
                {
                    if (applicableDescriptorMappings == null)
                    {
                        applicableDescriptorMappings = new Dictionary <TagHelperDescriptor, IReadOnlyList <TagMatchingRuleDescriptor> >();
                    }

                    applicableDescriptorMappings[descriptor] = applicableRules;
                }
            }

            if (applicableDescriptorMappings == null)
            {
                return(null);
            }

            var tagHelperBinding = new TagHelperBinding(
                tagName,
                attributes,
                parentTagName,
                applicableDescriptorMappings,
                _tagHelperPrefix);

            return(tagHelperBinding);
        }
예제 #7
0
        /// <summary>
        /// Gets all tag helpers that match the given HTML tag criteria.
        /// </summary>
        /// <param name="tagName">The name of the HTML tag to match. Providing a '*' tag name
        /// retrieves catch-all <see cref="TagHelperDescriptor"/>s (descriptors that target every tag).</param>
        /// <param name="attributes">Attributes on the HTML tag.</param>
        /// <param name="parentTagName">The parent tag name of the given <paramref name="tagName"/> tag.</param>
        /// <returns><see cref="TagHelperDescriptor"/>s that apply to the given HTML tag criteria.
        /// Will return <c>null</c> if no <see cref="TagHelperDescriptor"/>s are a match.</returns>
        public TagHelperBinding GetBinding(
            string tagName,
            IReadOnlyList <KeyValuePair <string, string> > attributes,
            string parentTagName)
        {
            if (!string.IsNullOrEmpty(_tagHelperPrefix) &&
                (tagName.Length <= _tagHelperPrefix.Length ||
                 !tagName.StartsWith(_tagHelperPrefix, StringComparison.OrdinalIgnoreCase)))
            {
                // The tagName doesn't have the tag helper prefix, we can short circuit.
                return(null);
            }

            IEnumerable <TagHelperDescriptor> descriptors;

            // Ensure there's a HashSet to use.
            if (!_registrations.TryGetValue(TagHelperMatchingConventions.ElementCatchAllName, out HashSet <TagHelperDescriptor> catchAllDescriptors))
            {
                descriptors = new HashSet <TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
            }
            else
            {
                descriptors = catchAllDescriptors;
            }

            // If we have a tag name associated with the requested name, we need to combine matchingDescriptors
            // with all the catch-all descriptors.
            if (_registrations.TryGetValue(tagName, out HashSet <TagHelperDescriptor> matchingDescriptors))
            {
                descriptors = matchingDescriptors.Concat(descriptors);
            }

            var tagNameWithoutPrefix = _tagHelperPrefix != null?tagName.Substring(_tagHelperPrefix.Length) : tagName;

            Dictionary <TagHelperDescriptor, IReadOnlyList <TagMatchingRuleDescriptor> > applicableDescriptorMappings = null;

            foreach (var descriptor in descriptors)
            {
                var applicableRules = descriptor.TagMatchingRules.Where(
                    rule => TagHelperMatchingConventions.SatisfiesRule(tagNameWithoutPrefix, parentTagName, attributes, rule));

                if (applicableRules.Any())
                {
                    if (applicableDescriptorMappings == null)
                    {
                        applicableDescriptorMappings = new Dictionary <TagHelperDescriptor, IReadOnlyList <TagMatchingRuleDescriptor> >();
                    }

                    applicableDescriptorMappings[descriptor] = applicableRules.ToList();
                }
            }

            if (applicableDescriptorMappings == null)
            {
                return(null);
            }

            var tagHelperBinding = new TagHelperBinding(
                tagName,
                attributes,
                parentTagName,
                applicableDescriptorMappings,
                _tagHelperPrefix);

            return(tagHelperBinding);
        }