Detecting if a URL scheme can be handled

Can we detect if a url with a custom URL scheme can be handled? Why yes. Yes we can.

registerProtocolHandler is an under used API. I love this API, it allows a web app to become the default system handler for safe URLs such as ‘mailto’, ‘irc’, ‘tel’, ‘sms’ as well as custom web+* types.

It unfortunately has a few issues:

  1. It’s not well supported on Mobile. PWAs in Chrome can register a protocol_handler, which is huge!
  2. People don’t really understand the scheme part of urls, and prefer https (more on this at the end)
  3. Developers don’t have the ability to determine if a custom scheme will resolve to anything useful for the user.

The last point is a big pain. If the user clicks a link on a web page you would expect it to do something. I think we can do something here, specifically present a common pattern that we can use when there is no app or web-site to handle the link.

A long time ago this was a solved issue. Chrome had an isProtocolHandlerRegistered method which would let you determine if there is something on the other end of the custom scheme. It was later removed from Chrome because it was removed from the Spec, and it was removed from the Spec for a variety of reasons including that the API could be as a finger-printing vector.

I’m opening up this can of worms again because of a challenge that I see with the "Follow" function in Mastodon and I think we can make it better.

Disclaimer: I know I will be conflating Mastodon and ActivityPub.

If you want to follow a person who is on a different Mastodon instance to you, you need to find their Follow page, enter your ID and instance name (i.e, @paul@status.kinlan.me) into the follow field, it will then redirect you back to your instance to let you follow that user.

It would be much nicer if you could click on a link to a Mastodon profile (say, on this web page) and it will take you directly to your instance to let you follow the user.

I could envisage a system where a link encoded as follows: <a href="web+follow:@paul@status.kinlan.me">Follow</a> (note web+follow instead of https), when clicked would take the user to their instance (or whatever ActivityPub service they have), with the @paul@status.kinlan.me populated so they can quickly follow me; and if the user doesn’t have an ActivityPub app or site available we can do something specific.

I will save the specifics for web+follow for another post. The pattern I am attempting to document is a common one for using custom schemes:

  1. User clicks a link that has a custom scheme
  2. Detect if the navigation failed (because there is no app)
  3. Present a UI to tell the user to do something.

I’ll give a preview: There is no solution that is as decisive isProtocolHandlerRegistered, all of the solutions are based on a heuristic.

What about the finger-printing concern? It might still be there. isProtocolHandlerRegistered didn’t have a required User-Gesture constraint so you could in theory scan a large set of protocols to determine sites the user might have registered. Any solutions here are gated on a user gesture such as a navigation via a link click.

This code assumes updateUI_NoHandler is a function that will update the UI to tell the user there is no site or app installed that can handle web+follow: links.

Attempt 1: Individual Click Handler

HTML

<a href="web+follow:@paul@status.kinlan.me" id="follow">
  Follow
</a>

JavaScript

addEventListener("load", (loadEvent) => {
  const follow = document.getElementById("follow");
  follow.addEventListener("click", (clickEvent) => {
    setTimeout(() => updateUI_NoHandler(), 1000);
  });
});

Pros:

  • Work’s in all browsers

Cons:

  • You need to augment your links with an ID and handle it in the click handler
  • Heuristic based – if the user is presented with a prompt (like in Safari and Firefox) then the UI will update.

Attempt 2: Global Click Handler

If you don’t want to augment your anchor, you could intercept all link clicks.

HTML

<a href="web+follow:@paul@status.kinlan.me">Follow</a>

JavaScript

addEventListener("load", (loadEvent) => {
  document.body.addEventListener("click", (clickEvent) => {
    const { target } = clickEvent;
    if (target.nodeName == "A" && target.href.startsWith("web+follow:")) {
      setTimeout(() => updateUI_NoHandler(), 1000);
    }
  });
});

Pros:

  • Work’s in all browsers
  • Doesn’t require you to augment your anchors.

Cons:

  • All the same problems you have tracking clicks globally on your page.
  • Doesn’t handle URLs from external apps
  • Heuristic based – if the user is presented with a prompt (like in Safari and Firefox) then the UI will update.

HTML

    <a href="web+follow:@paul@status.kinlan.me">Follow</a>

JavaScript

navigation.addEventListener("navigate", (event) => {
  if (event.destination.url.startsWith("web+follow")) {
    setTimeout(() => window.stop(), 1000)
  }
});

navigation.onnavigateerror = (event) => {
  if (event.error.message === "Navigation was aborted") {
    updateUI_NoHandler();
  }
};

Pros:

  • Doesn’t require you to augment any links in your HTML

Cons:

  • Blink only & Not stable yet.
  • Doesn’t handle

Attempt 4: Winner, but more complex.

This one is a little complex because it requires a server, but it handles all the cases and preserves URLs as HTTPS URLs.

Thanks to James Henstridge who mentioned that you might be able to 302 redirect to a custom scheme (such as mailto – try it) and if there is no app or site to handle the custom URL scheme the request doesn’t 404, it gets cancelled and the current page is left intact.

With this "no-op" side effect, you can then use a meta http-equiv="refresh" to attempt to redirect to the handler and if it’s available it will be followed, and if it isn’t you will remain on the current page.

<meta
 http-equiv="refresh"
 content="0; url=https://eastern-shimmering-car.glitch.me/web-follow"
/>
</head>
<body>
  You don't have a site installed to handle `web+follow`.
  Do x.y.z instead.
</body>

In this case "https://eastern-shimmering-car.glitch.me/web-follow"is a simple 302 redirect to web+follow:@paul@status.kinlan.me.

Pros:

  • Works
  • Keeps URLs as they are commonly understood

Cons:

  • Not super easy to set up.

Wrap up

I got to the end of this and I realised that I don’t think people will want custom schemes in their day to day lives, I wouldn’t encode a web+follow link in an email, I’d prefer to send a person to a web page. However custom schemes are incredibly useful as a tool for developers to be able to direct people into the sites and apps of their choice without knowing what site or app they are using while handling the case when there is no choice available.

Now it’s got me thinking that I could rebuild web intents off this.


Print Share Comment Cite Upload Translate
APA
Paul Kinlan | Sciencx (2024-03-29T15:20:24+00:00) » Detecting if a URL scheme can be handled. Retrieved from https://www.scien.cx/2022/11/21/detecting-if-a-url-scheme-can-be-handled/.
MLA
" » Detecting if a URL scheme can be handled." Paul Kinlan | Sciencx - Monday November 21, 2022, https://www.scien.cx/2022/11/21/detecting-if-a-url-scheme-can-be-handled/
HARVARD
Paul Kinlan | Sciencx Monday November 21, 2022 » Detecting if a URL scheme can be handled., viewed 2024-03-29T15:20:24+00:00,<https://www.scien.cx/2022/11/21/detecting-if-a-url-scheme-can-be-handled/>
VANCOUVER
Paul Kinlan | Sciencx - » Detecting if a URL scheme can be handled. [Internet]. [Accessed 2024-03-29T15:20:24+00:00]. Available from: https://www.scien.cx/2022/11/21/detecting-if-a-url-scheme-can-be-handled/
CHICAGO
" » Detecting if a URL scheme can be handled." Paul Kinlan | Sciencx - Accessed 2024-03-29T15:20:24+00:00. https://www.scien.cx/2022/11/21/detecting-if-a-url-scheme-can-be-handled/
IEEE
" » Detecting if a URL scheme can be handled." Paul Kinlan | Sciencx [Online]. Available: https://www.scien.cx/2022/11/21/detecting-if-a-url-scheme-can-be-handled/. [Accessed: 2024-03-29T15:20:24+00:00]
rf:citation
» Detecting if a URL scheme can be handled | Paul Kinlan | Sciencx | https://www.scien.cx/2022/11/21/detecting-if-a-url-scheme-can-be-handled/ | 2024-03-29T15:20:24+00:00
https://github.com/addpipe/simple-recorderjs-demo