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

fix: Adds support for standard protocol activations on wasm #19211

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
35 changes: 32 additions & 3 deletions doc/articles/features/protocol-activation.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,53 @@ If your target framework is Android 12, you must also add `Exported = true` to t

### WASM

WASM implementation uses the [`Navigator.registerProtocolHandler` API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler). This has several limitations for the custom scheme:
WASM implementation uses the [`Navigator.registerProtocolHandler` API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler).

This has several limitations when using a custom scheme:

- The custom scheme's name must begin with `web+`
- The custom scheme's name must include at least 1 letter after the `web+` prefix
- The custom scheme must have only lowercase ASCII letters in its name.

You can also use one of the following supported schemes instead:

- `bitcoin`
- `ftp`
- `ftps`
- `geo`
- `im`
- `irc`
- `ircs`
- `magnet`
- `mailto`
- `matrix`
- `mms`
- `news`
- `nntp`
- `openpgp4fpr`
- `sftp`
- `sip`
- `sms`
- `smsto`
- `ssh`
- `tel`
- `urn`
- `webcal`
- `wtai`
- `xmpp`

To register the custom theme, call the WASM-specific `Uno.Helpers.ProtocolActivation` API when appropriate to let the user confirm URI handler association:

```csharp
#if __WASM__
Uno.Helpers.ProtocolActivation.RegisterCustomScheme(
"web+myscheme",
"web+myscheme",
new System.Uri("http://localhost:55838/"),
"Can we handle web+myscheme links?");
#endif
```

The first argument is the scheme name, the second is the base URL of your application (it must match the current domain to be registered successfully), and the third is a text prompt, which will be displayed to the user to ask for permission.
The first argument is the scheme name, the second is the base URL of your application (it must match the current domain to be registered successfully), and the third is a text prompt, which will be displayed to the user to ask for permission (this does not work on all browsers e.g. edge).

When a link with the custom scheme gets executed, the browser will navigate to a your URL with additional `unoprotocolactivation` query string key, which will contain the custom URI. Uno internally recognizes this query string key and executes `OnActivated` appropriately.

Expand Down
77 changes: 52 additions & 25 deletions src/Uno.UWP/Helpers/ProtocolActivation.wasm.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.InteropServices.JavaScript;
Expand All @@ -12,6 +13,33 @@ public static partial class ProtocolActivation
{
internal const string QueryKey = "unoprotocolactivation";

private static readonly IEnumerable<string> _predefinedPrefixes = [
"bitcoin",
"ftp",
"ftps",
"geo",
"im",
"irc",
"ircs",
"magnet",
"mailto",
"matrix",
"mms",
"news",
"nntp",
"openpgp4fpr",
"sftp",
"sip",
"sms",
"smsto",
"ssh",
"tel",
"urn",
"webcal",
"wtai",
"xmpp"
];

/// <summary>
/// Registers a custom URI scheme for protocol activation on WASM.
/// </summary>
Expand All @@ -21,40 +49,39 @@ public static partial class ProtocolActivation
public static void RegisterCustomScheme(string scheme, Uri domain, string prompt)
{
// rules as per https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler

// The custom scheme's name begins with web+
if (!scheme.StartsWith("web+", StringComparison.Ordinal))
if (!_predefinedPrefixes.Contains(scheme))
{
throw new ArgumentException(
"Scheme must start with 'web+'",
nameof(scheme));
}

// The custom scheme's name includes at least 1 letter after the web+ prefix
if (scheme.Length == "web+".Length)
{
throw new ArgumentException(
"Scheme must include at least 1 letter after 'web+' prefix",
nameof(scheme));
}
// The custom scheme's name begins with web+
if (!scheme.StartsWith("web+", StringComparison.Ordinal))
{
throw new ArgumentException(
"Scheme must start with 'web+'",
nameof(scheme));
}

// The custom scheme has only lowercase ASCII letters in its name.
for (int i = "web+".Length; i < scheme.Length; i++)
{
if (scheme[i] is not (>= 'a' and <= 'z'))
// The custom scheme's name includes at least 1 letter after the web+ prefix
if (scheme.Length == "web+".Length)
{
throw new ArgumentException(
"Scheme must include only lowercase ASCII letters after " +
"the 'web+' prefix",
"Scheme must include at least 1 letter after 'web+' prefix",
nameof(scheme));
}
}

if (domain == null)
{
throw new ArgumentNullException(nameof(domain));
// The custom scheme has only lowercase ASCII letters in its name.
for (int i = "web+".Length; i < scheme.Length; i++)
{
if (scheme[i] is not (>= 'a' and <= 'z'))
{
throw new ArgumentException(
"Scheme must include only lowercase ASCII letters after " +
"the 'web+' prefix",
nameof(scheme));
}
}
}

ArgumentNullException.ThrowIfNull(domain);

if (!domain.IsAbsoluteUri)
{
throw new ArgumentException(
Expand Down
Loading