Skip to content
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

Set origin with NavigateToString #530

Open
jhandley opened this issue Oct 13, 2020 · 42 comments
Open

Set origin with NavigateToString #530

jhandley opened this issue Oct 13, 2020 · 42 comments
Assignees
Labels
feature request feature request tracked We are tracking this work internally.

Comments

@jhandley
Copy link

jhandley commented Oct 13, 2020

When setting HTML to display using ICoreWebView2::NavigateToString I would like to be able set the origin URL used by the browser for the page like you can do with the Android WebView method loadDataWithBaseURL

The origin URL would be used as the base for relative paths for resources. This allows you to set an HTML string that refers to images, scripts and stylesheets that are stored separately. In my case, on Android I set the base URL to a file:// and store images and stylesheets in that directory on disk.

I tried to simulate this using AddWebResourceRequestedFilter but the events only fire for http/https URLs so relative paths in HTML set by NavigateToString do not trigger these events.

I will try to make this work by writing out the HTML to a temporary file and use Navigate with a file:// url but I'd much rather avoid that using the same technique that I'm using on Android.

AB#29796279

@jhandley jhandley added the feature request feature request label Oct 13, 2020
@pagoe-msft pagoe-msft added the tracked We are tracking this work internally. label Oct 13, 2020
@pagoe-msft
Copy link

@jhandley - I've added this feature request to our backlog!

@billhenn
Copy link

I'm running into the same thing. I'm trying to use a WebView2 to browse a dynamically-generated "preview" file of some HTML. I'd ideally like to use NavigateToString() to keep the preview HTML from every being written to disk, but if I do so, there seems to be no way to dynamically provide it the CSS, images, etc. that are referenced in the HTML since AddWebResourceRequestedFilter doesn't seem to currently work for NavigateToString(). This then makes NavigateToString() effectively useless.

There are two issues here that I'd like to see resolved:

  1. AddWebResourceRequestedFilter should have the same feature set for NavigateToString() browsings as for files browsed on disk. If it did, we then could dynamically provide all CSS and images on the fly without ever having to even touch the disk.

  2. I love the idea of a "base URL" suggested by @jhandley since in some cases I can see scenarios where we do have the CSS/images already on disk and we want our virtual "preview" file to just think it's in a certain folder so that it can reference them the same as if if it were written on disk.

If both of these issues were resolved, that would make dynamic HTML viewing MUCH better using WebView2. Without those essential things (at least #1 but preferably both), we may have to go look at CefSharp instead, which we would prefer not to do. Thanks for considering adding them.

@jhandley
Copy link
Author

@billhenn I ended up using cpp-httplib to run a local web server to serve the content rather than writing it to disk. It is pretty lightweight and easy to set up. For dynamically generated html you can use httplib::Server::Get() to map a url to a lambda that generates the response. If you have files on disk you can use httplib::Server::set_mount_point() to map a url to a folder on disk and it will serve files from that folder. You can combine the two so that http://localhost:1234/foo.html maps to a lambda and any other request to http://localhost:1234 gets mapped to a folder on disk.

@ukandrewc
Copy link

ukandrewc commented Oct 29, 2020

@jhandley The events do fire for file:// protocol, but you need to navigate to a file.

@billhenn Can't you just include the resources in the string?

@billhenn
Copy link

Thanks for the tip @jhandley, but we write our software in C#, so I'm not sure that would work for us. Regardless, with these couple relative minor enhancements to WebView2, none of this extra effort would be needed and building dynamic previews would be really easy.

@ukandrewc - I'd rather not parse the HTML to try and locate CSS, images, etc. and then inject them inline somehow. I'd like to be able to serve them up on request from WebView2 using the existing events. It's just that the events aren't raised unless the HTML file is on the file system, which is the main issue.

@jhandley
Copy link
Author

@billhenn In C# you could probably do something similar with HttpListener but of course would be much simpler for all if WebView2 was enhanced.

@ukandrewc
Copy link

@billhenn After I thought that through, I did think it could get tricky ;-)

Do you mind if I ask why you don't want to use a temp file for your content?

@billhenn
Copy link

@ukandrewc In one usage scenario, I do write out a temp file for the HTML preview. However the problem is I need to leave it out there until the next render or when the app closes since the WebView processes it asynchronously. I had originally tried deleting it when the WebView2 indicated the content was rendered, but if another preview update request came in there in between, the WebView2 would sometimes show a not found error. Anyhow, all the messing around and managing of preview files gets annoying and if the app crashes, it leaves the preview file there. It all would be much cleaner to simply use NavigateToString() and be able to set the virtual base path.

@ukandrewc
Copy link

ukandrewc commented Oct 29, 2020

@billhenn In that case, you could use a skeleton HTML and fill the body from JS?

I do that for a print preview, where I write and navigate to a file on startup with:
<html><head><!--stylesheet, etc--></head><body></body></html>
then add body.innerHTML from ExecuteScriptAsync.

That way, I only navigate once and just change the content when needed.

@billhenn
Copy link

@ukandrewc I have a folder hierarchy so the preview could be in any folder. Ideally I wouldn't ever have to write anything to the file system myself if the requested features were added.

@ukandrewc
Copy link

@billhenn Fair enough, just a suggestion ;-)

@billhenn
Copy link

I appreciate the suggestions @ukandrewc. The app I'm making is a Markdown editor that needs a HTML preview. The Markdown files can be in a large folder hierarchy so I need to be able to preview them at any level. If I write a "~preview.html" file containing the Markdown-to-HTML rendering to the folder where the Markdown file lives, then WebView2 works great. But per what I described above, I really don't want to have to write anything to the file system since the "~preview.html" files stick in the folder until the next preview occurs.

@ukandrewc
Copy link

ukandrewc commented Oct 30, 2020

@billhenn Completely understand, I'm just used to working from files, so it seems counter intuitive (to me) to not use temp files. I also find an advantage of easier debugging when I've written some rubbish that's supposed to be HTML ;-)

The only other thing I do is to use a hash of the user path, and save to my own cache folder. Rather than saving to the user's folder.

I can see that what you are asking for, is the same as WebBrowser control, and would be an advantage.

@billhenn
Copy link

I just loaded up CefSharp (the Chromium WPF browser) and tried out this scenario with them. They have a browser.LoadHtml() method (similar to WebView2.NavigateToString()) and an overload accepts a URL. If you specify the URL, then it will treat that as the URL of the HTML string and it will proceed as if the HTML came from that source.

This is perfect because it allows me to provide the HTML result of my Markdown-to-HTML conversion and supply it right to the browser. Then by giving a "file://" URL of a faux .html file in the same folder as the original .md file, it automatically resolves all relative image/CSS/JS requests with no extra work by me. And their custom ResourceHandler class works too where I could intercept and provide any of those requests dynamically if I wanted to fulfill the request on my own, without ever hitting the file system.

Unfortunately I will have to switch to CefSharp until WebView2 is able to support a similar mechanism where NavigateToString supports a faux URL parameter, and AddWebResourceRequestedFilter works when using this mechanism.

@pagoe-msft - I'm not sure if you were following this thread, but please include my comments in your backlog notes. To sum up, the two things I need from WebView2 are:

  1. Add a NavigateToString(string htmlContent, Uri uri) overload where a faux Uri could be specified. The browser would act as if it came from that URL, even though I specified the HTML content myself.

  2. The filter added by AddWebResourceRequestedFilter needs to fully work when that new NavigateToString() overload is used so that we can dynamically supply resources for supplied HTML content. If we didn't intercept any requests, the default behavior would be to try and load images/CSS/JS relative to that supplied Uri folder.

Thank you for considering these enhancements.

@ukandrewc
Copy link

ukandrewc commented Oct 30, 2020

@billhenn really glad you've resolved your issue. Just in case you needed it, CEFSharp doesn't support proprietary video, audio codecs. Although you can buld the binaries yourself, and include them.

@champnic
Copy link
Member

champnic commented Nov 3, 2020

@billhenn Thanks for the details and your scenario, we've got them in our backlog item! Sorry we couldn't support this scenario yet, but glad you got unblocked with CefSharp.

@ukandrewc Thanks for the help and suggestions!

@cemocfr
Copy link

cemocfr commented Jan 12, 2021

Hi, we have the same need here! It would be very valuable for us if you could implement the proposed items from @billhenn

@romanan
Copy link

romanan commented Feb 7, 2021

@champnic I just want to double-check if the backlog item for this will allow HTML in NavigateToString be able to access local resources. The product I'm migrating from CEF uses Javascript as a scripting language, so previously a HTML file is generated that contains a Script element pointing to the script to run. I'm able to get this work if I use a static html and the Navigate function, but not when the same HTML is used for NavigateToString.

The HTML wrapper I'm testing looks like this:
<html>
<title>Javascript Holder</title>
<body id="body">
<script>
function addScript(value)
{
var s = document.createElement("script");
s.type = "text/javascript";
s.src = value;
document.getElementById("body").appendChild(s);
}
</script>
</body>
</html>

Using ExecuteScript to call addScript works with Navigate but not with NavigateToString. My workaround will just be to either ship the static HTML with the product or save it to disk at runtime.

@champnic
Copy link
Member

champnic commented Feb 8, 2021

@romanan Yes, part of this work will make accessing file:// resources easier from a NavigatToString. You can also try looking into the experimental Virtual Host Name API and see if that works for you here. It would allow you to specify a domain that is mapped to a folder on disc, and access resources in that location using http and the domain rather than file://.

@michaldivis
Copy link

Any progress on this? I'd love to use the NavigateToString feature but I need to be able to access local resources (css, images).

I'm saving the HTML to temporary files on the disk as a workaround for now, but it's clunky...

@champnic
Copy link
Member

We should begin work on this item in the next month or two.

@nishitha-burman
Copy link
Collaborator

Hello,
@jhandley @ukandrewc @billhenn @cemocfr @romanan @michaldivis

We are looking into providing a solution for this ask and have a couple of questions:

  1. Is this mostly for loading non-https content? Do you expect this to also load https content?
  2. Do you want to navigate to the base URL?
  3. If you do want to navigate to the base URL, do you want the data parameter to interact with the base URL (e.g. if you are providing JavaScript in the data parameter)?
  4. If WebResourceRequested can handle non-https content, would it address your scenario?

Thank you!

@jhandley
Copy link
Author

  1. My use case was for non-https content, specifically with a file:// url so that I could refer to local css/images/js files on the device via relative paths in the html content passed to navigateToString.
  2. Not sure what you mean by "navigate to the base URL". I want to load the HTML that is passed to NavigateToString. The url would be to a file that doesn't actually exist so there would be nothing to navigate to. It would behave exactly as if I had written the html content to a file and then navigated to the url of that file.
  3. It WebResourceRequested worked with file:// urls for local file that would address my concern.

I've switched jobs since working on this project and we long ago shipped the product with the workaround mentioned above so I'm not sure we would take advantage of this change at this point. @Oryxamus might have an opinion.

@michaldivis
Copy link

Hi @nishitha-burman,

  1. I want to load local css/js/image files, no https required.
  2. No.
  3. I don't want to navigate to the base URL.
  4. Yes, I think it would.

Here's my use case

I want to load resources from the local hard drive when I load HTML via the NavigateToString method like this:

<head>
    <link rel="stylesheet" href="C:\HtmlResources\bootstrap.min.css">
</head>
<body>
    <img class="img-fluid" src="C:\HtmlResources\image1.jpg" />
    <p><a href="myCustomLink">Click me</a></p>
</body>

or alternatively, using the file:// protocol

<head>
    <link rel="stylesheet" href="file://C:/HtmlResources/bootstrap.min.css">
</head>
<body>
    <img class="img-fluid" src="file://C:/HtmlResources/image1.jpg" />
    <p><a href="myCustomLink">Click me</a></p>
</body>

SetVirtualHostNameToFolderMapping

I've also tried using the SetVirtualHostNameToFolderMapping method:

webView2.CoreWebView2.SetVirtualHostNameToFolderMapping("myassets.local", @"C:/HtmlResources".FolderPath, CoreWebView2HostResourceAccessKind.Allow);

and then refering to my resources like this:

<head>
    <link rel="stylesheet" href="http://myassets.local/bootstrap.min.css">
</head>
<body>
    <img class="img-fluid" src="http://myassets.local/image1.jpg" />
    <p><a href="myCustomLink">Click me</a></p>
</body>

and this does load the resources, however, canceling navigation seems to be broken when using this method.

private void WebView2_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
{
    //problem: when a link is clicked, the Url is "about:blank#blocked" and I cannot retreive the link (myCustomLink)
    //problem: when a link is clicked, the navigation is not canceled event though I'm calling e.Cancel = true;
    e.Cancel = true;

    var linkName = Path.GetFileName(e.Uri);
    CustomHandleLinkClicked(linkName);
}

@Oryxamus
Copy link

I was mentioned by @jhandley as I'm working on the product that he was building in 2020. Just to follow up, we run a local web server now and it solved our needs. However, if we were to use the new functionality, we would need to be able to supply an array of folder paths as our product needs to resolve content in several locations. That may not be a common enough use case to design for, especially with the local web server workaround.

We have been very happy with WebView2 otherwise. It has allowed us to move a lot of UI development from MFC code to HTML/JavaScript. Continue the good work.

@cemocfr
Copy link

cemocfr commented Mar 11, 2022

Hi

  1. We use it for custom url schema so no https involved
  2. no
  3. we do not want to navigate to the base url (it is not really an url)
  4. I think it will solve our issue

Thank you

@nishitha-burman
Copy link
Collaborator

Thank you everyone for your reply! We are currently investigating this :)

@maxhrab
Copy link

maxhrab commented Apr 20, 2022

Hi

being able to set a base url when calling NavigateToString is the only feature I miss right now to port our app to using WebView2. Will this feature be implemented? Is there a schedule?

Thanks.

@mrsteamfist
Copy link
Contributor

mrsteamfist commented Apr 20, 2022

To access local resources (images, CSS, etc.. on the user's hard drive) in your HTML use Virtual Host Name Mapping with NavigateToString.
Here is an example:

webView.CoreWebView2.SetVirtualHostNameToFolderMapping(
    "appassets.example", "assets", CoreWebView2HostResourceAccessKind.DenyCors);
string htmlContent =
@"
    <head><link rel='stylesheet' href ='http://appassets.example/run.css' /></head>
    <body>
        <img src='http://appassets.example/grill.png' />
        <p><a href='http://appassets.example/winrt_test.txt'> Click me</a></p>
    </body>
";
webview.NavigateToString(htmlContent);

@nishitha-burman
Copy link
Collaborator

@maxhrab does this address the scenario(s) you are trying to address?

@maxhrab
Copy link

maxhrab commented Apr 20, 2022

Hi, thanks for the suggestion, but I'm afraid this won't help me. I use a html launcher page that writes a few settings to window.localStorage and then sets location.href to go to the real target url. this won't work with WebView2 because the page origin is about:blank. If there is an alternative way to access the local storage I could work around this.

@mrsteamfist
Copy link
Contributor

Hi, thanks for the suggestion, but I'm afraid this won't help me. I use a html launcher page that writes a few settings to window.localStorage and then sets location.href to go to the real target url. this won't work with WebView2 because the page origin is about:blank. If there is an alternative way to access the local storage I could work around this.

At this point, we do not support local storage to NavigateToString: localStorage
It sounds like you don't know at runtime where that content lives. My suggestion is to use a HostObject to find the content and then setup either a virtual host or a WebResourceRequest.

@aluhrs13
Copy link
Contributor

@maxhrab - You should be able to use Chromium DevTools Protocol to modify local storage directly if that's valuable - https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage/#method-setDOMStorageItem

@michaldivis
Copy link

...

private void WebView2_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
{
    //problem: when a link is clicked, the Url is "about:blank#blocked" and I cannot retreive the link (myCustomLink)
    //problem: when a link is clicked, the navigation is not canceled event though I'm calling e.Cancel = true;
    e.Cancel = true;

    var linkName = Path.GetFileName(e.Uri);
    CustomHandleLinkClicked(linkName);
}

Any progress on this?

The issue still seems to be there for Microsoft.Web.WebView2 version 1.0.1587.40.

@seandr
Copy link

seandr commented May 10, 2023

Would really love to see NavigateToString with a baseUrl parameter. Is this still in the works? To answer the above questions for my scenario:

  1. This is for local files only.
  2. No
  3. N/a
  4. We load arbitrary HTML, so using WebResourceRequested requires us to handle css, image, scripts, fonts, and anything else a page might require. Essentially, you're asking us to duplicate a bunch functionality in the browser. I suppose it's possible, but it's quite ugly compared to providing a single baseUrl parameter.

@aluhrs13
Copy link
Contributor

For folks adding their scenarios to this - If you can also include why SetVirtualHostnameToFolderMapping doesn't work for your scenario?

@seandr

@seandr
Copy link

seandr commented May 11, 2023

SetVirtualHostnameToFolderMapping doesn't work for us because it doesn't handle relative paths.

Our app allows users to create presentations that can include any html page. Pages may include dynamic content, and they typically reference other files - e.g. images, styles, etc. Presentations can be copied to arbitrary locations on disk or uploaded to any web server. References must therefore be relative since the absolute path can include any directory or domain.

We can't require absolute virtual references because this is incompatible with previous versions of our app that supported relative paths (using the IE control on Windows). Also, we are cross platform so the pages must display properly in other platform's web controls.

P.S. Correction from my previous post: we need a solution that works locally and over https.

@krzysiek-b
Copy link

@champnic

We should begin work on this item in the next month or two.

Any update on this?

@champnic
Copy link
Member

@krzysiek-b We had begun the investigation for this item, though I think it got deprioritized a bit with the introduction of SetVirtualHostnameToFolderMapping, which handled a lot of the cases (though not all). @aluhrs13 or @nishitha-burman will have the latest on it's current status.

@zcbenz
Copy link

zcbenz commented Mar 31, 2024

@aluhrs13 All other system webview solutions have the equivalent of setting base url when loading html: webkitgtk, WKWebView, and even IWebBrowser2. And the lack of feature in WebView2 causes problems for people writing cross platform software, they now must write 2 different implementations: one that just sets base url, and one that uses SetVirtualHostnameToFolderMapping.

@Foda
Copy link

Foda commented Apr 9, 2024

For folks adding their scenarios to this - If you can also include why SetVirtualHostnameToFolderMapping doesn't work for your scenario?

@seandr

@aluhrs13 Hello! This would be useful for us on .NET MAUI since we ship a dev UI toolkit and don't have control over the HTML passed to the webview. We can modify the incoming HTML string to include a base element, but all other platforms support navigation to string using baseURL.

@RobotGizmo
Copy link

We also don't have control over the HTML being displayed. Having a baseURL is a must-have for us unfortunately.

PureWeen added a commit to dotnet/maui that referenced this issue Apr 23, 2024
…bViewSource (#21892)

### Description of Change

This PR removes the use of a 2nd "hidden" WebView2 that was used to
parse and add a HTML `base` tag to the `head` tag when setting the HTML
source of a WebView to a string.

This was done by appending the `base` tag script to the start of the
user's HTML string, which the WebView then adds into the `head` element.
While this is technically not valid HTML, all current browsers correct
this behavior.

This is a work-around for the lack of being able to set the base URL
when navigating to a string using WebView2
(MicrosoftEdge/WebView2Feedback#530).

As a bonus, using `HtmlWebViewSource` should now be 2x faster 😅

### Issues Fixed

Fixes #21631
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request feature request tracked We are tracking this work internally.
Projects
None yet
Development

No branches or pull requests