dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.45k stars 10.03k forks source link

.NET 8 Blazor - Enhanced Navigation DOM Patching Algorithm and Link Elements #54250

Closed sbwalker closed 6 months ago

sbwalker commented 8 months ago

Is there an existing issue for this?

Describe the bug

The Oqtane Framework (https://github.com/oqtane/oqtane.framework) is a CMS built on Blazor which allows users to assign different themes (ie.layouts) to different pages within a site. Internally this means that it needs to modify the CSS style sheets which are included in the head of the page.

In interactive render mode, it uses custom JS Interop methods to accomplish this which works perfectly. As part of the migration to static rendering in .NET 8, it needed to provide the same functionality in static render mode. This was accomplished by including some logic in App.razor which generates the link elements and including them in the page. However, some very strange behavior was observed when running the application.

The application is using Enhanced Navigation and when a user would navigate between pages with different themes, the link elements would be updated in the DOM however the CSS styles would not be properly applied. In the browser console, a message would be displayed:

image

"None of the “sha512” hashes in the integrity attribute match the content of the subresource" ... which generally means that you did not specify the correct integrity value for the remote resource you are trying to load. This was very strange as the integrity attributes in the DOM were correct for the remote resources. Refreshing the browser manually resolved the problem and would allow the page styles to display properly.

This initially led me to believe that I needed to apply the data-enhance-nav to the links in the site. And indeed this seemed to resolve the properly... however it did so at the expense of Enhanced Navigation... and since there are many links within a site - some included by the system and some within user provided content - it is not feasible to use data-enhance-nav as a solution.

This got me thinking more about Enhanced Navigation. I realized that it is using a "patching algorithm" to update the DOM. I have some experience with patching algorithms when it comes to browsers, as I have run into some problems in this area previously. This raised my suspicion that the patching algorithm may be responsible for the problem I was observing.

When I originally developed the JS Interop methods in Oqtane to manage link elements in interactive Blazor, I ran into issues relating to how browser's react to modifications to link elements.

If you want to modify a link element in the DOM which already has integrity and/or origin attributes assigned to it, you need to ensure that you remove those attributes first before you change the href attribute. If you don't, the browser will immediately notice the change to the href and it will use the existing the integrity and origin attributes to validate the resource - which will fail, which means the browser will not load the CSS document. And it does not matter if your logic then updates the integrity and crossorigin values to the correct values - the browser will ignore them because it only monitors changes to the href attribute.

So to test my hypothesis I modified the logic in my application to generate link elements in a different format - including the integrity and origin attributes at the beginning of the element and href at the end. This would test if the Blazor DOM patching algorithm is sequential in nature and simply processes attributes from left to right. My theory was that if it processed the integrity and origin attributes BEFORE the href attribute, the patching algorithm would apply the attributes in the correct order for the browser to process them.

And indeed this turned out to be correct. Changing the link format from:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.2/cyborg/bootstrap.min.css" integrity="sha512-RfNxVfFNFgqk9MXO4TCKXYXn9hgc+keHCg3xFFGbnp2q7Cifda+YYzMTDHwsQtNx4DuqIMgfvZead7XOtB9CDQ==" crossorigin="anonymous"type="text/css">

to

<link rel="stylesheet" integrity="sha512-RfNxVfFNFgqk9MXO4TCKXYXn9hgc+keHCg3xFFGbnp2q7Cifda+YYzMTDHwsQtNx4DuqIMgfvZead7XOtB9CDQ==" crossorigin="anonymous" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.2/cyborg/bootstrap.min.css" type="text/css">

fully resolved the issue.

So this was good news... however it makes me wonder how many other DOM patching scenarios are impacted by the sequential approach taken with the current algorithm. When unexplainable rendering issues occur it is very difficult to diagnose them and significant time can be spent trying to track down a root cause.

I am logging this issue here partly because I am hoping the DOM patching can be improved for link elements... but partly to document this issue and solution in order to help other developers who encounter this mysterious behavior.

Expected Behavior

No response

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

8.0

Anything else?

No response

sbwalker commented 8 months ago

I have not tested this yet... but I expect it is a problem with script elements as well which include integrity and crossorigin attributes in addition to the src attribute.