Skip to content

[Blazor][WebView] Abstractions #30456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 92 commits into from
Mar 5, 2021
Merged

[Blazor][WebView] Abstractions #30456

merged 92 commits into from
Mar 5, 2021

Conversation

javiercn
Copy link
Member

WIP This is the initial setup for the Blazor abstractions for running on a WebView. Just sharing the branch for now, details and a design write-up to come a bit later.

The goal is to provide the fundamental primitives for Blazor WebView as well as a "Headless" webview that we can use in our unit tests.

@javiercn
Copy link
Member Author

javiercn commented Feb 25, 2021

@SteveSandersonMS @Eilon here's the branch with my WIP FYI, I'm not looking 4 feedback just yet, I'll ping the PR when I get this in the shape I want.

@Pilchie Pilchie added the area-blazor Includes: Blazor, Razor Components label Feb 25, 2021
@SteveSandersonMS SteveSandersonMS force-pushed the javiercn/blazor-webview branch from d7fbb6d to 01dffd1 Compare March 2, 2021 10:31
@SteveSandersonMS
Copy link
Member

@javiercn @Eilon I had to rebase this onto main (and hence force-push) to pick up all the recent TypeScript changes before doing new work in that area. Please be sure to get the latest version of this branch locally before doing any further work on it, otherwise you'll have some trouble with pushing commits later.

@javiercn
Copy link
Member Author

javiercn commented Mar 2, 2021

@javiercn @Eilon I had to rebase this onto main (and hence force-push) to pick up all the recent TypeScript changes before doing new work in that area. Please be sure to get the latest version of this branch locally before doing any further work on it, otherwise you'll have some trouble with pushing commits later.

Sounds good to me

private readonly Dictionary<string, int> _componentIdBySelector = new();
private readonly Dispatcher _dispatcher;
private readonly IpcSender _ipcSender;
private long nextRenderBatchId = 1;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this here?

For Blazor Server I can understand, but for Blazor WebView I think we can rely on a simple ACK without having to keep track of the id

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just helps us discover more quickly if we have any bugs related to out-of-order delivery.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can this situation arise?

Copy link
Member

@SteveSandersonMS SteveSandersonMS Mar 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have bugs, for example something to do with how we dispatch the messages or executions onto the dispatcher, which might allow for things to get out of order. It shouldn't happen, but there's no harm making sure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW in my experience I've never seen such a bug while working on MBB and haven't seen it from customers, but perhaps it can more easily be an issue while we develop the code, so it seems like a good precaution. I cannot imagine such a bug would be easy to debug when things simply explode and behave oddly.

if (message != null && message.StartsWith(_ipcMessagePrefix, StringComparison.Ordinal))
{
var messageAfterPrefix = message.AsSpan(_ipcMessagePrefix.Length);
var parsed = (JsonElement[])JsonSerializer.Deserialize(messageAfterPrefix, typeof(JsonElement[]), JsonSerializerOptionsProvider.Options);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to think about here is that we are double-serializing JSON data when we do JS Interop

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree. I think we can make this interop mechanism more low-level and performant later if we want to. Hopefully it's all very much contained to this one file (and the equivalent on the .ts side) so it should be easy to reason about any changes.

public IpcReceiver(WebViewManager manager)
{
_manager = manager;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should reconsider the direction of this dependency.

The WebViewManager creates and manages the IPCReceiver, but then the receiver calls back to the manager and both reference each other.

I think we might be better of if we follow an alternative approach:

  1. Handle initialization outside the IpcReceiver.
  2. Instead of dispatching the "AttachToPage" we expose an "Initialize" event that the manager subscribes to from outside.

That way all interactions always go Manager -> (IPCXXX) and there is no coupling both ways.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely, sounds good to me! I agree it's a bit unexpected that it relies on an internal method at the moment. The only drawback that concerns me about using an event is that it's hard to deal with asynchrony and exceptions, but we can figure something out.


namespace Microsoft.AspNetCore.Components.WebView.WebView2
{
public class WebView2WebViewManager : WebViewManager
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point we'll need to decide whether we want all this stuff to be public. It's not really an interesting part of the API surface from a public consumption perspective, yet it allows us to have common code between WPF, WinForms, and .NET MAUI.

No need to decide anything now. Keeping this in a separate assembly will help us ensure we have good layering for now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, the layering is fine for now.

Later on we should consider replacing the M.A.C.WebView.WebView2 package with use of a shared-source file for the WebView2WebViewManager abstraction. It would make no difference to functionality, but would mean we just don't have to ship an extra publicly-consumable package for what is intended as an internal abstraction.

await _currentPageContext.Renderer.AddRootComponentAsync(
rootComponent.ComponentType,
selector,
rootComponent.Parameters);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about automatically re-rendering the components in this situation.

There's a chance that rootComponent.Parameters contains objects that have been mutated and given that we might go to a different host page, there is a chance that the same root components might not apply to the new page?

Copy link
Member

@SteveSandersonMS SteveSandersonMS Mar 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have more of an end-to-end scenario in mind that would be a good example of this?

I would expect in most cases people won't use the root-component parameters at all, and in the cases where they do, it's so that different host pages can have different global settings, or so that you could have a single root component shared across Blazor WebAssembly/Server/WebView and pass them some info or flags that differ based on the hosting environment. In these scenarios it wouldn't be something you'd try to mutate, and if you did try and nothing happened, that seems like it would be fine.

given that we might go to a different host page

As it stands, the BlazorWebView controls only let you configure a single fixed host page per control, so the scenario doesn't apply. You can write to the HostPage property on the control, but once it's initialized, it ignores any such changes. We might want to make it throw if you try to write to that property after initialization.

protected bool TryGetResponseContent(string uri, bool isPageLoad, out int statusCode, out string statusMessage, out Stream content, out string headers)
=> _staticContentProvider.TryGetResponseContent(uri, isPageLoad, out statusCode, out statusMessage, out content, out headers);

internal async Task AttachToPageAsync(string baseUrl, string startUrl)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, "spicy" thought is that we need to have a "root component" SetParameters API here to dispatch parameter updates to top-level components.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if this becomes a common scenario then I definitely agree. However the usability of mutating root component parameters from a WPF/WinForms control might not be great, and there will always be alternatives like sending commands via some DI service. I'd suggest we wait to see some specific customer demand here before implementing this.

@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Mar 4, 2021

@javiercn @Eilon We should decide what milestone we're trying to reach before merging this into main. There's a lot of worthy functionality in here already, so I'd say it would be good to merge soon.

What are the remaining pieces we want to have in before merging?

  • The main one that comes to mind for me is more tests. There's opportunity to backfill a load of unit tests for WebViewManager, plus @javiercn is looking into E2E automation.
  • We need to add copyright headers to all the files. There might be other minor code formatting things to sort out, such as any missing XML docs, and the PublicAPI files.
  • We should check with @dougbu if there are any concerns we need to address about adding Windows-specific projects to the build. Doug, this PR is going to add a WPF project and a WinForms project, both targeting $(DefaultNetCoreTargetFramework)-windows. We hopefully exclude them from building on non-Windows machines - does that look correct and adequate to you?

Anything else?

@Eilon
Copy link
Contributor

Eilon commented Mar 4, 2021

@SteveSandersonMS spot on. BTW for WinForms I took the lazy route and used your RootComponent type. Certainly not meant to be a final API, so thanks for updating it!

Regarding merging into main: Let's discuss today and we should easily be able to assign tasks to get them done, especially the more trivial ones (headers, doc comments, etc.).

@dougbu
Copy link
Contributor

dougbu commented Mar 4, 2021

any concerns we need to address about adding Windows-specific projects to the build

We have a few projects that can only be built in msbuild e.g. https://github.com/dotnet/aspnetcore/pull/30456/files#diff-366b25472963c6b33188d84139f6c8997298c5adc857a2624ab0d26a2d87baf0R9-R14 and a few others that are platform-specific. As long as the projects listed in https://github.com/dotnet/aspnetcore/pull/30456/files#diff-366b25472963c6b33188d84139f6c8997298c5adc857a2624ab0d26a2d87baf0 work on ARM64, you should be good to go. But, beware of other projects that may reference those ones and make sure such references are also conditional.

@javiercn javiercn force-pushed the javiercn/blazor-webview branch from cedd707 to 7a0be28 Compare March 5, 2021 12:14
@javiercn javiercn marked this pull request as ready for review March 5, 2021 16:42
@javiercn javiercn requested review from dougbu and a team as code owners March 5, 2021 16:42
Copy link
Contributor

@pranavkm pranavkm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#yolo

@javiercn javiercn merged commit c899471 into main Mar 5, 2021
@javiercn javiercn deleted the javiercn/blazor-webview branch March 5, 2021 17:52
@Eilon
Copy link
Contributor

Eilon commented Mar 5, 2021

I retroactively sign off 😀

@ghost
Copy link

ghost commented Mar 5, 2021

Hi @Eilon. It looks like you just commented on a closed PR. The team will most probably miss it. If you'd like to bring something important up to their attention, consider filing a new issue and add enough details to build context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants