-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Fix for #5523 MauiWebView not loading local files on Windows #7672
Conversation
6f4ae3e
to
afb2939
Compare
For the record I have noticed a related issue in WebView2 that still prevents this from working in all scenarios. In WinUI3 I've found that a complex client-side app will somtimes fail to load properly, no errors in console or failed network requests, but it behaves as though it has failed to load files being fetched via xhr requests. After doing some digging, I discovered that this issue is resolved if you install Edge from the Beta channel, so I assume a fix for that is in the pipeline in WebView2 package and will be released in next cycle. I think what I've implemented here is independent of that and this shouldn't prevent it being merged? |
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
Noticed on original issue @jsuarezruiz added an example of loading a local MauiAsset file that does work on Windows. Whilst this works, it requires code in the page codebehind to fetch the asset file contents and load as a string into the WebView. This PR would allow local files to work with no code required from the user, keeping it consistent with the other platforms, and the WebView documentation. |
@mattleibow does this need any changes for unpackaged support? |
I think so as the Package type will throw. This can easily be tested by setting the WindowsPackageType to None and editing the commandName value in the launchSettings.json from msixPackage to project. I collected the types I know plus instructions here #3166 |
Thanks @mattleibow & @Redth. I will make the changes and push another commit for review. |
@mattleibow Is there a way to check at runtime if the current Application is packaged or unpackaged? e.g. will this work? if (Package.Current == null)
{ //use AppContext.BaseDirectory } |
That will throw an exception, so you catch that. We do this in essentials: https://github.com/dotnet/maui/blob/main/src/Essentials/src/AppInfo/AppInfo.uwp.cs#L46-L64 |
OK have made the changes for Unpackaged app support and successfully tested in both packaged/unpackaged samples. To test, I hijacked the credits page on the TestRunner project to show a local sample HTML page from /Resources/Raw folder, set as MauiAsset. <ItemGroup>
...
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup> I also removed the Navigating event, as for some reason this was cancelling navigation and trying to open the URI provided in the external browser in the codebehind. This means the regular credits.html embedded resource in dotnet maui/main never loads, by the way. Had some trouble getting the Unpackaged app to run following steps at your link as kept running into this issue but finally got it work with the following configuration using an X64 build configuration (as can't run Any CPU in self contained mode): <PropertyGroup Condition="$(TargetFramework.Contains('-windows'))">
<WindowsPackageType>None</WindowsPackageType>
<AppxPackage>false</AppxPackage>
<PublishAppxPackage>false</PublishAppxPackage>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
</PropertyGroup> @mattleibow can you review, please? |
Come thing is iffy with this PR. Billions of changes. I am wondering where it is supposed to go and maybe a merge happened from a wrong branch? |
@mattleibow Have tidied it up. |
CoreWebView2.SetVirtualHostNameToFolderMapping( | ||
LocalHostName, | ||
ApplicationPath, | ||
Web.WebView2.Core.CoreWebView2HostResourceAccessKind.Allow); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is a potential security problem here.
Consider this scenario:
- App uses MAUI WebView and calls
LoadHtml("some html blah blah", baseUrl: null);
- This causes MAUI to load the specified HTML, and enable local file mapping for the app, thus enabling the WebView to load arbitrary files from within the app contents. This part is pretty much fine, because it's generally assumed that if you're loading HTML yourself, you know what's in it, and that it's considered sanitized and safe
- Later, the app calls some
Navigate
API on the WebView, causing the WebView to go to some other URL (say,https://malware.example.com
- But because the mapping is still enabled, I believe this could cause the malicious site to load arbitrary content from within the app. I'm not sure what access the malicious site would have to the contents of what it loaded, but it could potentially cause unintended things to be invoked.
So, I think this change needs to be aware of navigation events and that the access/mapping be changed when the WebView navigates away from one of the "safe" locations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point. I'll change it to hook into Navigation events, check target URL for the local hostname OnNavigating, and call CoreWebView2.ClearVirtualHostNameToFolderMapping(..) if attempting to navigate to any other URL. I guess I will need to re-enable that mapping on any attempt to navigate back to the local host URL, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@breenbob yeah I think some logic like that would fix this. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, have added this and tested. Looks like a) it works and b) it was needed.
To test I created a sample web page that I hosted somewhere, with the following body:
<body>
<h3>Test Page</h3>
<p>I am a remote page.</p>
<iframe src="https://appdir/simple.html" height="200" width="300" title="App Dir"></iframe>
<br />
<a href="https://appdir/simple.html">Link back</a>
</body>
I added a link to this URL to my simple.html test page.
With the code changes applied, my test page loads fine, as before. I click the link to the external site, that loads fine, but the iframe pointing to local appdir simple html file fails to load:
Good so far. I click the link to take me back to https://appdir/simple.html (i.e. so I am navigating forwards, not using back button) and it works fine.
I am conscious that there are potentially multiple calls to Map the virtual hostname now, as it is done in LoadUrl/Html and in NavigationStarted, but it doesn't seem to cause any issue, and unmapping once still unmaps. I had tried to track its status with a boolean flag but got a bit unwieldy, and WebView2 has no method to check what hosts are mapped.
Only peculiar thing I noticed, as you can see in this gif, is that when navigating back both the NavigationFailed and NavigationSucceeded events fire in the WebViewHandler.Windows.cs
. Failed first, with host name unknown, then succeeded. Actual navigation works fine.
Out of curisoity, I tried loading the external page with the call to ClearVirtualHostNameToFolderMapping in NavigationStarted commented out, just to see, and the iframe is now able to load the appdir simple html page:
So clearing the virtual host mapping is 100% necessary. Hopefully good to go now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we good to go?
@breenbob Quick question: will your fix also allow local .mp4 files to be loaded into |
@MollsAndHersh Yes this PR should allow that to succeed, provided the HTML file that is trying to load the video is also local, not just the video file itself. Also important is that the local HTML page doesn't have a As I understand it, anything with MauiAsset set on its Build Action will copy to the root of the app directory on compilation, and so could be loaded in the WebView with a relative URL after this PR goes in. What you have to be careful with is if you have a folder structure for your files. If so, you should really use the /Resources/Raw folder to maintain that folder structure, by ensuring this entry is in your Maui app csproj: <ItemGroup>
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup> This copies anything from /Raw folder to the app installation root directory, but maintains the subfolder structure. This PR has been tagged for SR2 release which has a target completion date of Wednesday, so with any luck, will be available soon! |
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
If we want this on sr2 it should be target net6.0 branch |
@rmarinho Done - now up to date with the net6.0 branch. |
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for getting back so quickly to me @breenbob. I'm excited to try this out :) I'm wondering exactly "how" I pull down the latest changes into my project so I get the new updated WebView code? Do I just grab the newest Maui on a Nuget feed? Should I just install the latest Visual Studio preview? (Currently I'm on Microsoft Visual Studio Community 2022 (64-bit) - Preview Version 17.3.0 Preview 2.0.) Or do I use Maui-Check? I'm sorta lost on the correct way forward. Can you provide me any clues? Thanks. |
@MollsAndHersh When the SR2 package is released yeah it's just a matter of updating the Maui Nuget packages to the latest version. SR1 is version 6.0.400, so it will be the next version after that. You don't need to wait for this though, you can work around this in your code by using the handlers: In xaml of your page: <WebView
HandlerChanged="WebView_HandlerChanged"
Source="MyLocalMauiAssetPage.html" /> You should continue setting Source on the WebView in Xaml so that your page continues to load on the Android/iOS. Adding the below code to your page code behind will then allow it to also load on Windows: #if WINDOWS
private static readonly System.Lazy<bool> _isPackagedAppLazy = new System.Lazy<bool>(() =>
{
try
{
if (Windows.ApplicationModel.Package.Current != null)
return true;
}
catch
{
// no-op
}
return false;
});
private static bool IsPackagedApp => _isPackagedAppLazy.Value;
// Allow for packaged/unpackaged app support
string ApplicationPath => IsPackagedApp
? Windows.ApplicationModel.Package.Current.InstalledLocation.Path
: System.AppContext.BaseDirectory;
#endif
private async void WebView_HandlerChanged(object sender, EventArgs e)
{
#if WINDOWS
await ((sender as WebView).Handler.PlatformView as Microsoft.Maui.Platform.MauiWebView).EnsureCoreWebView2Async();
((sender as WebView).Handler.PlatformView as Microsoft.Maui.Platform.MauiWebView).CoreWebView2.SetVirtualHostNameToFolderMapping(
"localhost",
ApplicationPath,
Microsoft.Web.WebView2.Core.CoreWebView2HostResourceAccessKind.Allow);
((sender as WebView).Handler.PlatformView as Microsoft.Maui.Platform.MauiWebView).LoadUrl($"https://localhost/MyLocalMauiAssetPage.html");
#endif
} Just replace <ItemGroup>
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup> Once the new Nugets are released and you update to them, you should be able to remove this code and just stick with |
@breenbob Beautiful... this works now. Thank you so much!!! For anyone else reading this, I had to make two small changes in order to get the code to compile...
Thanks again Bob... very grateful. |
Looking at the PR comments regarding security, would it be possible to modify the files locally and so consequently make the application load this modified file? That is, I wonder how the files are stored in the systems and signed in a way or another. I know this is a more general question. I'm in fact trying to find documentation regarding this on:
I would've actually asked also about the same thing as @Eilon hadn't it been asked already. :) So, I don't intend to hijack this thread, but wonder where could one check these things. I can't seem to find documentation regarding these issues. I can imagine these guestions are out of scope of Maui documentation. (I have found various soures explaining tampering in Android, detecting debugger/debuggability, SafetyNet etc.) |
@veikkoeeva in general, if the application's files can be modified, then that's a different problem, if it's even a problem at all. There's usually no concern about a user modifying their own app because they could just do whatever they want to begin with! They don't have to "hack" their own app if they could just do the "malicious" thing to begin with. The main concern is if an app can be "tricked" into running someone else's code, or if you deliberately have the app run someone else's code with limited privileges, you want to prevent that code from elevating its privileges and gaining access to more than you intended. |
Description of Change
Modified the MauiWebView control on Windows to map the Application AppX install directory to a virtual folder using the WebView2.CoreWebView.SetVirtualHostNameToFolderMapping method.
The existing implementation used the "ms-appx-web:///" URI scheme, which according to these issues is not being implemented in WebView2 for WinUI3.
microsoft/microsoft-ui-xaml#1967
MicrosoftEdge/WebView2Feedback#37
Issues Fixed
Fixes #5523