From f66003c8118cc8c7ca21df09c060e08151a3672c Mon Sep 17 00:00:00 2001 From: Sergey Kalinichenko Date: Sun, 15 Oct 2023 19:47:12 -0400 Subject: [PATCH 1/4] Supporting pseudo-element attribute in css link fallback (#38146) --- src/Mvc/Mvc.TagHelpers/src/LinkTagHelper.cs | 28 +- .../src/PublicAPI.Unshipped.txt | 2 + ...kTagHelper_FallbackWithPseudoJavaScript.js | 1 + .../Mvc.TagHelpers/test/LinkTagHelperTest.cs | 313 ++++++++++++++++++ ...Site.HtmlGeneration_Home.Link.Encoded.html | 75 +++++ ...ationWebSite.HtmlGeneration_Home.Link.html | 76 +++++ .../Views/HtmlGeneration_Home/Link.cshtml | 167 ++++++++++ 7 files changed, 659 insertions(+), 3 deletions(-) create mode 100644 src/Mvc/Mvc.TagHelpers/src/compiler/resources/LinkTagHelper_FallbackWithPseudoJavaScript.js diff --git a/src/Mvc/Mvc.TagHelpers/src/LinkTagHelper.cs b/src/Mvc/Mvc.TagHelpers/src/LinkTagHelper.cs index f7950deae6d6..f5535b7745cd 100644 --- a/src/Mvc/Mvc.TagHelpers/src/LinkTagHelper.cs +++ b/src/Mvc/Mvc.TagHelpers/src/LinkTagHelper.cs @@ -29,12 +29,15 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers; [HtmlTargetElement("link", Attributes = FallbackHrefExcludeAttributeName, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("link", Attributes = FallbackTestClassAttributeName, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("link", Attributes = FallbackTestPropertyAttributeName, TagStructure = TagStructure.WithoutEndTag)] +[HtmlTargetElement("link", Attributes = FallbackTestPseudoElementAttributeName, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("link", Attributes = FallbackTestValueAttributeName, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("link", Attributes = AppendVersionAttributeName, TagStructure = TagStructure.WithoutEndTag)] public class LinkTagHelper : UrlResolutionTagHelper { private static readonly string FallbackJavaScriptResourceName = typeof(LinkTagHelper).Namespace + ".compiler.resources.LinkTagHelper_FallbackJavaScript.js"; + private static readonly string FallbackWithPseudoJavaScriptResourceName = + typeof(LinkTagHelper).Namespace + ".compiler.resources.LinkTagHelper_FallbackWithPseudoJavaScript.js"; private const string HrefIncludeAttributeName = "asp-href-include"; private const string HrefExcludeAttributeName = "asp-href-exclude"; @@ -44,6 +47,7 @@ public class LinkTagHelper : UrlResolutionTagHelper private const string FallbackHrefExcludeAttributeName = "asp-fallback-href-exclude"; private const string FallbackTestClassAttributeName = "asp-fallback-test-class"; private const string FallbackTestPropertyAttributeName = "asp-fallback-test-property"; + private const string FallbackTestPseudoElementAttributeName = "asp-fallback-test-pseudo-element"; private const string FallbackTestValueAttributeName = "asp-fallback-test-value"; private const string AppendVersionAttributeName = "asp-append-version"; private const string HrefAttributeName = "href"; @@ -199,6 +203,14 @@ public LinkTagHelper( [HtmlAttributeName(FallbackTestPropertyAttributeName)] public string FallbackTestProperty { get; set; } + /// + /// The CSS pseudo-element name to use for the fallback test. + /// May be used in conjunction with and , + /// and either or . + /// + [HtmlAttributeName(FallbackTestPseudoElementAttributeName)] + public string FallbackTestPseudoElement { get; set; } + /// /// The CSS property value to use for the fallback test. /// Must be used in conjunction with and , @@ -358,9 +370,12 @@ private void BuildFallbackBlock(TagHelperAttributeList attributes, TagHelperCont // tag to load the fallback stylesheet if the test CSS property value is found to be false, // indicating that the primary stylesheet failed to load. // GetEmbeddedJavaScript returns JavaScript to which we add '"{0}","{1}",{2});' + // or '"{0}","{1}",{2},{3});' depending an optional pseudo-element parameter. + var usePseudoElement = FallbackTestPseudoElement != null; builder .AppendHtml(""); + builder.AppendHtml("\""); + if (usePseudoElement) + { + builder + .AppendHtml(", \"") + .AppendHtml(JavaScriptEncoder.Encode(FallbackTestPseudoElement)) + .AppendHtml("\""); + } + builder.AppendHtml(");"); } private bool HasStyleSheetLinkType(TagHelperAttributeList attributes) diff --git a/src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt index 3b900f5c4f03..dc241f896473 100644 --- a/src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt @@ -1,3 +1,5 @@ #nullable enable ~Microsoft.AspNetCore.Mvc.TagHelpers.InputTagHelper.FormName.get -> string ~Microsoft.AspNetCore.Mvc.TagHelpers.InputTagHelper.FormName.set -> void +~Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper.FallbackTestPseudoElement.get -> string +~Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper.FallbackTestPseudoElement.set -> void diff --git a/src/Mvc/Mvc.TagHelpers/src/compiler/resources/LinkTagHelper_FallbackWithPseudoJavaScript.js b/src/Mvc/Mvc.TagHelpers/src/compiler/resources/LinkTagHelper_FallbackWithPseudoJavaScript.js new file mode 100644 index 000000000000..e987f3a1e991 --- /dev/null +++ b/src/Mvc/Mvc.TagHelpers/src/compiler/resources/LinkTagHelper_FallbackWithPseudoJavaScript.js @@ -0,0 +1 @@ +!function(a,b,c,d,j){var e,f=document,g=f.getElementsByTagName("SCRIPT"),h=g[g.length-1].previousElementSibling,i=f.defaultView&&f.defaultView.getComputedStyle?f.defaultView.getComputedStyle(h,j):h.currentStyle;if(i&&i[a]!==b)for(e=0;e")}(); \ No newline at end of file diff --git a/src/Mvc/Mvc.TagHelpers/test/LinkTagHelperTest.cs b/src/Mvc/Mvc.TagHelpers/test/LinkTagHelperTest.cs index 9126be503d75..9539c0ac5e81 100644 --- a/src/Mvc/Mvc.TagHelpers/test/LinkTagHelperTest.cs +++ b/src/Mvc/Mvc.TagHelpers/test/LinkTagHelperTest.cs @@ -285,6 +285,130 @@ public static TheoryData RunsWhenRequiredAttributesArePresent_Data tagHelper.SuppressFallbackIntegrity = false; } }, + // asp-fallback-test-pseudo-element Attribute is present + { + new TagHelperAttributeList + { + new TagHelperAttribute("asp-fallback-href", "test.css"), + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + new TagHelperAttribute("asp-fallback-test-property", "visibility"), + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden") + }, + tagHelper => + { + tagHelper.FallbackHref = "test.css"; + tagHelper.FallbackTestClass = "hidden"; + tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + } + }, + { + new TagHelperAttributeList + { + new TagHelperAttribute("asp-fallback-href-include", "*.css"), + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + new TagHelperAttribute("asp-fallback-test-property", "visibility"), + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden") + }, + tagHelper => + { + tagHelper.FallbackHrefInclude = "*.css"; + tagHelper.FallbackTestClass = "hidden"; + tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + } + }, + // File Version + { + new TagHelperAttributeList + { + new TagHelperAttribute("asp-fallback-href", "test.css"), + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + new TagHelperAttribute("asp-fallback-test-property", "visibility"), + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden"), + new TagHelperAttribute("asp-append-version", "true") + }, + tagHelper => + { + tagHelper.FallbackHref = "test.css"; + tagHelper.FallbackTestClass = "hidden"; + tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + tagHelper.AppendVersion = true; + } + }, + { + new TagHelperAttributeList + { + new TagHelperAttribute("asp-fallback-href-include", "*.css"), + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + new TagHelperAttribute("asp-fallback-test-property", "visibility"), + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden"), + new TagHelperAttribute("asp-append-version", "true") + }, + tagHelper => + { + tagHelper.FallbackHrefInclude = "*.css"; + tagHelper.FallbackTestClass = "hidden"; + tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + tagHelper.AppendVersion = true; + } + }, + // asp-suppress-fallback-integrity Attribute true + { + new TagHelperAttributeList + { + new TagHelperAttribute("asp-fallback-href", "test.css"), + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + new TagHelperAttribute("asp-fallback-test-property", "visibility"), + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden"), + new TagHelperAttribute("asp-append-version", "true"), + new TagHelperAttribute("asp-suppress-fallback-integrity", "true") + }, + tagHelper => + { + tagHelper.FallbackHref = "test.css"; + tagHelper.FallbackTestClass = "hidden"; + tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + tagHelper.AppendVersion = true; + tagHelper.SuppressFallbackIntegrity = true; + } + }, + // asp-suppress-fallback-integrity Attribute false + { + new TagHelperAttributeList + { + new TagHelperAttribute("asp-fallback-href", "test.css"), + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + new TagHelperAttribute("asp-fallback-test-property", "visibility"), + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden"), + new TagHelperAttribute("asp-append-version", "true"), + new TagHelperAttribute("asp-suppress-fallback-integrity", "false") + }, + tagHelper => + { + tagHelper.FallbackHref = "test.css"; + tagHelper.FallbackTestClass = "hidden"; + tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + tagHelper.AppendVersion = true; + tagHelper.SuppressFallbackIntegrity = false; + } + }, }; } } @@ -420,6 +544,7 @@ public void PreservesOrderOfNonHrefAttributes() { "asp-fallback-href", "test.css" }, { "asp-fallback-test-class", "hidden" }, { "asp-fallback-test-property", "visibility" }, + { "asp-fallback-test-pseudo-element", "before" }, { "asp-fallback-test-value", "hidden" }, }); var output = MakeTagHelperOutput("link", @@ -433,6 +558,7 @@ public void PreservesOrderOfNonHrefAttributes() helper.FallbackHref = "test.css"; helper.FallbackTestClass = "hidden"; helper.FallbackTestProperty = "visibility"; + helper.FallbackTestPseudoElement = "before"; helper.FallbackTestValue = "hidden"; helper.Href = "test.css"; @@ -515,6 +641,64 @@ public static TheoryData DoesNotRunWhenARequiredAttributeIsMissing_Data tagHelper.FallbackTestProperty = "visibility"; tagHelper.FallbackTestValue = "hidden"; } + }, + { + new TagHelperAttributeList + { + // This is commented out on purpose: new TagHelperAttribute("asp-fallback-href", "test.css"), + // Note asp-href-include attribute isn't included. + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + new TagHelperAttribute("asp-fallback-test-property", "visibility"), + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden") + }, + tagHelper => + { + // This is commented out on purpose: tagHelper.FallbackHref = "test.css"; + tagHelper.FallbackTestClass = "hidden"; + tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + } + }, + { + new TagHelperAttributeList + { + new TagHelperAttribute("asp-fallback-href", "test.css"), + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + // This is commented out on purpose: new TagHelperAttribute("asp-fallback-test-property", "visibility"), + // Note asp-href-include attribute isn't included. + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden") + }, + tagHelper => + { + tagHelper.FallbackHref = "test.css"; + tagHelper.FallbackTestClass = "hidden"; + // This is commented out on purpose: tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + } + }, + { + new TagHelperAttributeList + { + // This is commented out on purpose: new TagHelperAttribute("asp-fallback-href-include", "test.css"), + new TagHelperAttribute("asp-fallback-href-exclude", "**/*.min.css"), + new TagHelperAttribute("asp-fallback-test-class", "hidden"), + new TagHelperAttribute("asp-fallback-test-property", "visibility"), + new TagHelperAttribute("asp-fallback-test-pseudo-element", "before"), + new TagHelperAttribute("asp-fallback-test-value", "hidden") + }, + tagHelper => + { + // This is commented out on purpose: tagHelper.FallbackHrefInclude = "test.css"; + tagHelper.FallbackHrefExclude = "**/*.min.css"; + tagHelper.FallbackTestClass = "hidden"; + tagHelper.FallbackTestProperty = "visibility"; + tagHelper.FallbackTestPseudoElement = "before"; + tagHelper.FallbackTestValue = "hidden"; + } } }; } @@ -880,6 +1064,135 @@ public void RenderLinkTags_FallbackHref_WithFileVersion_EncodesAsExpected() Assert.Equal(expectedContent, content); } + [Fact] + public void RenderLinkTags_FallbackHref_WithFileVersionAndPseudoElement() + { + // Arrange + var expectedPostElement = Environment.NewLine + + ""; + var context = MakeTagHelperContext( + attributes: new TagHelperAttributeList + { + { "asp-append-version", "true" }, + { "asp-fallback-href-include", "**/fallback.css" }, + { "asp-fallback-test-class", "hidden" }, + { "asp-fallback-test-property", "visibility" }, + { "asp-fallback-test-pseudo-element", "before" }, + { "asp-fallback-test-value", "hidden" }, + { "href", "/css/site.css" }, + { "rel", new HtmlString("stylesheet") }, + }); + var output = MakeTagHelperOutput( + "link", + attributes: new TagHelperAttributeList + { + { "rel", new HtmlString("stylesheet") }, + }); + var globbingUrlBuilder = new Mock( + new TestFileProvider(), + Mock.Of(), + PathString.Empty); + globbingUrlBuilder.Setup(g => g.BuildUrlList(null, "**/fallback.css", null)) + .Returns(new[] { "/fallback.css" }); + + var helper = GetHelper(); + helper.AppendVersion = true; + helper.Href = "/css/site.css"; + helper.FallbackHrefInclude = "**/fallback.css"; + helper.FallbackTestClass = "hidden"; + helper.FallbackTestProperty = "visibility"; + helper.FallbackTestPseudoElement = "before"; + helper.FallbackTestValue = "hidden"; + helper.GlobbingUrlBuilder = globbingUrlBuilder.Object; + + // Act + helper.Process(context, output); + + // Assert + Assert.Equal("link", output.TagName); + Assert.Equal("/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", output.Attributes["href"].Value); + Assert.Equal(expectedPostElement, output.PostElement.GetContent()); + } + + [Fact] + public void RenderLinkTags_FallbackHref_WithFileVersionAndPseudoElement_EncodesAsExpected() + { + // Arrange + var expectedContent = "" + + Environment.NewLine + + ""; + var mixed = new DefaultTagHelperContent(); + mixed.Append("HTML encoded"); + mixed.AppendHtml(" and contains \"quotes\""); + var context = MakeTagHelperContext( + attributes: new TagHelperAttributeList + { + { "asp-append-version", "true" }, + { "asp-fallback-href-include", "**/fallback.css" }, + { "asp-fallback-test-class", "hidden" }, + { "asp-fallback-test-property", "visibility" }, + { "asp-fallback-test-pseudo-element", "before" }, + { "asp-fallback-test-value", "hidden" }, + { "encoded", new HtmlString("contains \"quotes\"") }, + { "href", "/css/site.css" }, + { "literal", "all HTML encoded" }, + { "mixed", mixed }, + { "rel", new HtmlString("stylesheet") }, + }); + var output = MakeTagHelperOutput( + "link", + attributes: new TagHelperAttributeList + { + { "encoded", new HtmlString("contains \"quotes\"") }, + { "literal", "all HTML encoded" }, + { "mixed", mixed }, + { "rel", new HtmlString("stylesheet") }, + }); + var globbingUrlBuilder = new Mock( + new TestFileProvider(), + Mock.Of(), + PathString.Empty); + globbingUrlBuilder.Setup(g => g.BuildUrlList(null, "**/fallback.css", null)) + .Returns(new[] { "/fallback.css" }); + + var helper = GetHelper(); + + helper.AppendVersion = true; + helper.FallbackHrefInclude = "**/fallback.css"; + helper.FallbackTestClass = "hidden"; + helper.FallbackTestProperty = "visibility"; + helper.FallbackTestPseudoElement = "before"; + helper.FallbackTestValue = "hidden"; + helper.GlobbingUrlBuilder = globbingUrlBuilder.Object; + helper.Href = "/css/site.css"; + + // Act + helper.Process(context, output); + + // Assert + Assert.Equal("link", output.TagName); + Assert.Equal("/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", output.Attributes["href"].Value); + var content = HtmlContentUtilities.HtmlContentToString(output, new HtmlTestEncoder()); + Assert.Equal(expectedContent, content); + } + [Fact] public void RendersLinkTags_GlobbedHref_WithFileVersion() { diff --git a/src/Mvc/test/Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Link.Encoded.html b/src/Mvc/test/Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Link.Encoded.html index 00ae75a1c44b..a386cea5945e 100644 --- a/src/Mvc/test/Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Link.Encoded.html +++ b/src/Mvc/test/Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Link.Encoded.html @@ -140,6 +140,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mvc/test/Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Link.html b/src/Mvc/test/Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Link.html index eeff44b25541..7cf0056a85e9 100644 --- a/src/Mvc/test/Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Link.html +++ b/src/Mvc/test/Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Link.html @@ -141,6 +141,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mvc/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/Link.cshtml b/src/Mvc/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/Link.cshtml index 9bc535fd5f10..c84ac48fc83f 100644 --- a/src/Mvc/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/Link.cshtml +++ b/src/Mvc/test/WebSites/HtmlGenerationWebSite/Views/HtmlGeneration_Home/Link.cshtml @@ -242,6 +242,173 @@ asp-fallback-test-value="hidden" asp-append-version="true"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 30216b13e541d1c20f570a917ebf5997a739cb9d Mon Sep 17 00:00:00 2001 From: Sergey Kalinichenko Date: Tue, 17 Oct 2023 22:19:14 -0400 Subject: [PATCH 2/4] Added non-minified version of the fallback script --- ...kTagHelper_FallbackWithPseudoJavaScript.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/Mvc/Mvc.TagHelpers/src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js diff --git a/src/Mvc/Mvc.TagHelpers/src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js b/src/Mvc/Mvc.TagHelpers/src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js new file mode 100644 index 000000000000..c27f3eb91bb2 --- /dev/null +++ b/src/Mvc/Mvc.TagHelpers/src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +( + /** + * This function finds the previous element (assumed to be meta) and tests its current CSS style using the passed + * values and pseudo-element, to determine if a stylesheet was loaded. If not, this function loads + * the fallback stylesheet via document.write. + * + * @param {string} cssTestPropertyName - The name of the CSS property to test. + * @param {string} cssTestPropertyValue - The value to test the specified CSS property for. + * @param {string[]} fallbackHrefs - The URLs to the stylesheets to load in the case the test fails. + * @param {string} extraAttributes - The extra attributes string that should be included on the generated link tags. + * @param {string} pseudoElement - A string specifying the pseudo-element to match. + */ + function loadFallbackStylesheet(cssTestPropertyName, cssTestPropertyValue, fallbackHrefs, extraAttributes) { + var doc = document, + // Find the last script tag on the page which will be this one, as JS executes as it loads + scriptElements = doc.getElementsByTagName("SCRIPT"), + // Find the meta tag before this script tag, that's the element we're going to test the CSS property on + meta = scriptElements[scriptElements.length - 1].previousElementSibling, + // Get the current style of the meta tag starting with standards-based API and falling back to <=IE8 API + metaStyle = (doc.defaultView && doc.defaultView.getComputedStyle) ? + doc.defaultView.getComputedStyle(meta, pseudoElement) : meta.currentStyle, + i; + + if (metaStyle && metaStyle[cssTestPropertyName] !== cssTestPropertyValue) { + for (i = 0; i < fallbackHrefs.length; i++) { + doc.write(''); + } + } +})(); From c9d598ccca31119a6967ed49088778c97c75d99d Mon Sep 17 00:00:00 2001 From: Sergey Kalinichenko Date: Mon, 20 Nov 2023 22:18:30 -0500 Subject: [PATCH 3/4] Updated PublicAPI.Unshipped --- src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..b7054afd9c5d 100644 --- a/src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.TagHelpers/src/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ #nullable enable +~Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper.FallbackTestPseudoElement.get -> string +~Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper.FallbackTestPseudoElement.set -> void From 336db710cd33aaf023220450e08fd83ccc4dfa94 Mon Sep 17 00:00:00 2001 From: Sergey Kalinichenko Date: Fri, 15 Dec 2023 20:27:52 -0500 Subject: [PATCH 4/4] Fixed the non-minified javascript file (added a missing parameter and fixed its comment) --- .../src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mvc/Mvc.TagHelpers/src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js b/src/Mvc/Mvc.TagHelpers/src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js index c27f3eb91bb2..1e68a667897f 100644 --- a/src/Mvc/Mvc.TagHelpers/src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js +++ b/src/Mvc/Mvc.TagHelpers/src/js/LinkTagHelper_FallbackWithPseudoJavaScript.js @@ -10,9 +10,9 @@ * @param {string} cssTestPropertyValue - The value to test the specified CSS property for. * @param {string[]} fallbackHrefs - The URLs to the stylesheets to load in the case the test fails. * @param {string} extraAttributes - The extra attributes string that should be included on the generated link tags. - * @param {string} pseudoElement - A string specifying the pseudo-element to match. + * @param {string} pseudoElement - An optional string specifying the CSS pseudo-element to match. */ - function loadFallbackStylesheet(cssTestPropertyName, cssTestPropertyValue, fallbackHrefs, extraAttributes) { + function loadFallbackStylesheet(cssTestPropertyName, cssTestPropertyValue, fallbackHrefs, extraAttributes, pseudoElement) { var doc = document, // Find the last script tag on the page which will be this one, as JS executes as it loads scriptElements = doc.getElementsByTagName("SCRIPT"),