It's frustrating that VSTO, Office Task Pane Add-ins, and Office Content Add-ins cover different capabilities but aren't designed to work together. Also, the use case seems so natural (present an html-based report) but for some reason it's not supported.
Eugene gave good advice, but I wanted to add my findings in case you are searching for the same thing I was, a web browser control embedded in a spreadsheet. If that's a strict requirement, your (not-so-great) options are:
Embed a winforms web control (or a WPF one hosted in a winforms control) to a spreadsheet through worksheet.AddControl. This doesn't require modifying the registry like you do to manually add an ActiveX WebBrowser to the spreadsheet. However, it won't render if the spreadsheet is zoomed in or out, and you have to roll your own move/resize controls.
Use a custom ActiveX control and OleObjects.Add to embed a winforms/WPF web browser in the sheet. This resolves the zoom issues but I won't begin to elaborate on the issues with creating ActiveX controls in 2023.
Create a custom web browser control and "embed" it "into the spreadsheet" by making the Workbook's HWND its parent using Win32. You'd have to manually handle the control's visibility based on what spreadsheet is showing, etc.
Make an Office Content Add-in a "sister" application to your VSTO one. I didn't get to look into how to deploy these together or start them together. Also, the UX flow won't be great because for some reason Microsoft have decided they won't allow the user to programmatically open instances of the Content Add-in. It's required to go through the ribbon.
However, if this isn't an issue for you, I was able to get a
prototype working where my VSTO app told my Content Add-in's
embedded web browser what html to render, using the method detailed
in this page.
From the author:
The solution is to use the Document as a communication medium. In the
particular case we used CustomXMLParts in the document. Here is how it
would work:
One add-in would need to send an update to the other, so it would write a CustomXMLPart with a specific namespace and a “context”
(basically, I am the TaskPane communicating) to the document.
Both add-ins will have a window.setInterval() thread running to check the documents for CustomXMLParts in that given namespace.
The timer on the Content Add-in would fire, find the new customXMLPart from the taskpane, read the contents and then update
itself as needed and finally, delete the CustomXMLPart.