Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@

<ItemGroup>
<EmbeddedResource Include="WasmScripts\AppManifest.js" />

<Content Include="..\LinkedFiles\WebContent\**\*.*">
<Link>WebContent\%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

<WasmShellExtraFilesToDeploy Include="..\LinkedFiles\WebContent\**\*.*">
<TargetPath>WebContent/%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</WasmShellExtraFilesToDeploy>
</ItemGroup>

<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ internal static partial class NativeMethods
[JSImport("globalThis.Microsoft.UI.Xaml.Controls.WebView.getAttribute")]
internal static partial string? GetAttribute(ElementId htmlId, string attribute);

[JSImport("globalThis.Microsoft.UI.Xaml.Controls.WebView.getPackageBase")]
internal static partial string GetPackageBase();

[JSImport("globalThis.Microsoft.UI.Xaml.Controls.WebView.setAttribute")]
internal static partial void SetAttribute(ElementId htmlId, string attribute, string value);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Threading.Tasks;
using Microsoft.Web.WebView2.Core;
using Uno.UI.Xaml.Controls;
using Windows.Storage;
using Windows.Storage.Helpers;
using static __Microsoft.UI.Xaml.Controls.NativeWebView;

#if WASM_SKIA
Expand Down Expand Up @@ -41,24 +43,24 @@ public NativeWebView(CoreWebView2 coreWebView, ElementId elementId)
}

[JSExport]
internal static void DispatchLoadEvent(ElementId elementId)
internal static void DispatchLoadEvent(ElementId elementId, string? absoluteUrl)
{
if (_elementIdToNativeWebView.TryGetValue(elementId, out var nativeWebView))
{
nativeWebView.OnNavigationCompleted(nativeWebView._coreWebView, EventArgs.Empty);
nativeWebView.OnNavigationCompleted(nativeWebView._coreWebView, absoluteUrl);
}
}

public string DocumentTitle => NativeMethods.GetDocumentTitle(_elementId) ?? "";

private void OnNavigationCompleted(object sender, EventArgs e)
private void OnNavigationCompleted(object sender, string? absoluteUrl)
{
if (_coreWebView is null)
{
return;
}

var uriString = NativeMethods.GetAttribute(_elementId, "src");
var uriString = string.IsNullOrEmpty(absoluteUrl) ? NativeMethods.GetAttribute(_elementId, "src") : absoluteUrl;
Uri uri = CoreWebView2.BlankUri;
if (!string.IsNullOrEmpty(uriString))
{
Expand Down Expand Up @@ -97,14 +99,38 @@ private void ScheduleNavigationStarting(string? url, Action loadAction)
public void ProcessNavigation(Uri uri)
{
var uriString = uri.OriginalString;

// Handle virtual host mapping for local assets
if (!string.IsNullOrEmpty(uri.Host) &&
_coreWebView.HostToFolderMap.TryGetValue(uri.Host.ToLowerInvariant(), out var folderName))
{
var relativePath = uri.AbsolutePath.TrimStart('/');
var mappedPath = $"{folderName.TrimEnd('/')}/{relativePath}";

if (!string.IsNullOrEmpty(relativePath))
{
var packageBase = NativeMethods.GetPackageBase();
uriString = $"{packageBase.TrimEnd('/')}/{mappedPath.TrimStart('/')}";

if (!string.IsNullOrEmpty(uri.Query))
{
uriString += uri.Query;
}
if (!string.IsNullOrEmpty(uri.Fragment))
{
uriString += uri.Fragment;
}
}
}

ScheduleNavigationStarting(uriString, () => NativeMethods.SetAttribute(_elementId, "src", uriString));
OnNavigationCompleted(this, EventArgs.Empty);
OnNavigationCompleted(this, null);
}

public void ProcessNavigation(string html)
{
ScheduleNavigationStarting(null, () => NativeMethods.SetAttribute(_elementId, "srcdoc", html));
OnNavigationCompleted(this, EventArgs.Empty);
OnNavigationCompleted(this, null);
}

public void ProcessNavigation(HttpRequestMessage httpRequestMessage)
Expand All @@ -131,7 +157,7 @@ public void OnLoaded()
{
_elementIdToNativeWebView.TryAdd(_elementId, this);
NativeMethods.SetupEvents(_elementId);
DispatchLoadEvent(_elementId);
DispatchLoadEvent(_elementId, null);
}

public void OnUnloaded()
Expand Down
154 changes: 89 additions & 65 deletions src/Uno.UI/ts/Windows/UI/Xaml/Controls/WebView.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,92 @@
namespace Microsoft.UI.Xaml.Controls {

export class WebView {
private static unoExports: any;

public static buildImports(assembly: string) {
if (!WebView.unoExports) {
(<any>window.Module).getAssemblyExports(assembly)
.then((e: any) => {
WebView.unoExports = e.Microsoft.UI.Xaml.Controls.NativeWebView;
});
}
}

static reload(htmlId: string): void {
(<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow.location.reload();
}

static stop(htmlId: string): void {
(<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow.stop();
}

static goBack(htmlId: string): void {
(<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow.history.back();
}

static goForward(htmlId: string): void {
(<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow.history.forward();
}

static executeScript(htmlId: string, script: string): string {
return ((<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow as any).eval(script);
}

static getDocumentTitle(htmlId: string): string {
return (<HTMLIFrameElement>document.getElementById(htmlId)).contentDocument.title;
}

static setAttribute(htmlId: string, name: string, value: string) {
(<HTMLIFrameElement>document.getElementById(htmlId)).setAttribute(name, value);
}

static getAttribute(htmlId: string, name: string): string {
return (<HTMLIFrameElement>document.getElementById(htmlId)).getAttribute(name);
}

static initializeStyling(htmlId: string) {
const iframe = document.getElementById(htmlId) as HTMLIFrameElement;
iframe.style.backgroundColor = "transparent";
iframe.style.border = "0";
}

static setupEvents(htmlId: string) {
const iframe = <HTMLIFrameElement>document.getElementById(htmlId);
iframe.addEventListener('load', WebView.onLoad);
}

static cleanupEvents(htmlId: string) {
const iframe = <HTMLIFrameElement>document.getElementById(htmlId);
iframe.removeEventListener('load', WebView.onLoad);
}

private static onLoad(event: Event) {
const iframe = event.currentTarget as HTMLIFrameElement;
WebView.unoExports.DispatchLoadEvent(iframe.id);
}
}
export class WebView {
private static unoExports: any;
private static cachedPackageBase: string | null = null;

public static buildImports(assembly: string) {
if (!WebView.unoExports) {
(<any>window.Module).getAssemblyExports(assembly)
.then((e: any) => {
WebView.unoExports = e.Microsoft.UI.Xaml.Controls.NativeWebView;
});
}
}

static reload(htmlId: string): void {
(<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow.location.reload();
}

static stop(htmlId: string): void {
(<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow.stop();
}

static goBack(htmlId: string): void {
(<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow.history.back();
}

static goForward(htmlId: string): void {
(<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow.history.forward();
}

static executeScript(htmlId: string, script: string): string {
return ((<HTMLIFrameElement>document.getElementById(htmlId)).contentWindow as any).eval(script);
}

static getDocumentTitle(htmlId: string): string {
return (<HTMLIFrameElement>document.getElementById(htmlId)).contentDocument.title;
}

static setAttribute(htmlId: string, name: string, value: string) {
(<HTMLIFrameElement>document.getElementById(htmlId)).setAttribute(name, value);
}

static getAttribute(htmlId: string, name: string): string {
return (<HTMLIFrameElement>document.getElementById(htmlId)).getAttribute(name);
}

static initializeStyling(htmlId: string) {
const iframe = document.getElementById(htmlId) as HTMLIFrameElement;
iframe.style.backgroundColor = "transparent";
iframe.style.border = "0";
}

static getPackageBase(): string {

Check notice on line 54 in src/Uno.UI/ts/Windows/UI/Xaml/Controls/WebView.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI/ts/Windows/UI/Xaml/Controls/WebView.ts#L54

The class method 'getPackageBase' must be marked either 'private', 'public', or 'protected'
if (WebView.cachedPackageBase !== null) {
return WebView.cachedPackageBase;
}

const pathsToCheck = [
...Array.from(document.getElementsByTagName('script')).map(s => s.src),
];

for (const path of pathsToCheck) {
const m = path?.match(/\/package_[^\/]+/);
if (m) {
const packageBase = "./" + m[0].substring(1);
WebView.cachedPackageBase = packageBase;
return packageBase;
}
}

WebView.cachedPackageBase = ".";
return ".";
}

static setupEvents(htmlId: string) {
const iframe = <HTMLIFrameElement>document.getElementById(htmlId);
iframe.addEventListener('load', WebView.onLoad);
}

static cleanupEvents(htmlId: string) {
const iframe = <HTMLIFrameElement>document.getElementById(htmlId);
iframe.removeEventListener('load', WebView.onLoad);
}

private static onLoad(event: Event) {
const iframe = event.currentTarget as HTMLIFrameElement;
const absoluteUrl = iframe.contentWindow.location.href;
WebView.unoExports.DispatchLoadEvent(iframe.id, absoluteUrl);
}
}
}
Loading