How to detect browser's protocol handlers?
Asked Answered
T

13

94

I have created a custom URL protocol handler.

http://

mailto://

custom://

I have registered a WinForms application to respond accordingly. This all works great.

But I would like to be able to gracefully handle the case where the user doesn't have the custom URL protocol handler installed, yet.

In order to be able to do this I need to be able to detect the browser's registered protocol handlers, I would assume from JavaScript. But I have been unable to find a way to poll for the information. I am hoping to find a solution to this problem.

Thanks for any ideas you might be able to share.

Thi answered 7/5, 2009 at 20:10 Comment(3)
I think this would only be possible in chrome (i.e. XPCOM, ActiveX, etc.) code. Otherwise, it would be a privacy issue ("We've detected you use Eudora. Switch to FooMail today!"). But please clarify what browser(s)/OS(es) you're interested in.Rosemaria
Good point, but I'd be happy to know something is registered to handle my proprietary protocol acsfs:// Windows IE, FireFox, and ideally SafariThi
Have you solve already solved this problem?Fringe
P
40

This would be a very, very hacky way to do this... but would this work?

  • Put the link in as normal...
  • But attach an onclick handler to it, that sets a timer and adds an onblur handler for the window
  • (in theory) if the browser handles the link (application X) will load stealing the focus from the window...
  • If the onblur event fires, clear the timer...
  • Otherwise in 3-5seconds let your timeout fire... and notify the user "Hmm, looks like you don't have the Mega Uber Cool Application installed... would you like to install it now? (Ok) (Cancel)"

Far from bulletproof... but it might help?

Plagiarize answered 7/5, 2009 at 20:41 Comment(7)
:D It's a clever idea. Seems like there would be a way because it seems like a common need.Thi
In Firefox on Mac (maybe more browsers), the window loses focus and onblur fires even though the application with the custom protocol fails to launch.Carduaceous
This is a decent solution for Chrome, since it doesn't have any kind of error handling for protocols. This has been used in conjunction with detection of other browsers here: rajeshsegu.com/2012/09/browser-detect-custom-protocols/…Leodora
protocol detection has been a major headache lately. ;) The above method seems to work somewhat... using the rajesh's live example found at rajeshsegu.com/fun/code/browser/detect.html I am getting "true" into Chrome. But if I refactor so that there is no results() function (which in turn uses an alert box-- a blocking call) and let it just return a boolean, I am getting false. I sense somehow that the blocking alert is also a hacky way to force timing somehow... any insight, @scunliffe?Hudnut
This method does work in Chrome, except in the case that the user selected to remember their answer, using Win8 and does not have the application. In this case the blur event happens even though the application can't launchInion
For those looking for that rajeshsegu page which is no longer available, I fetched the source code using archive.org and imported it into a jsfiddle here: jsfiddle.net/stefansundin/0uca4h2oIntramural
I wonder if this is the only way, even though it's 2022. However, it seems to work well in Chrome.Overbite
C
20

HTML5 defines Custom scheme and content handlers (to my knowledge Firefox is the only implementor so far), but unfortunately there is currently no way to check if a handler already exists—it has been proposed, but there was no follow-up. This seems like a critical feature to use custom handlers effectively and we as developers should bring attention to this issue in order to get it implemented.

Citify answered 25/4, 2011 at 18:25 Comment(0)
W
19

There's no great cross-browser way to do this. In IE10+ on Win8+, a new msLaunchUri api enables you to launch a protocol, like so:

navigator.msLaunchUri('skype:123456', 
  function() 
  { 
    alert('success');
  }, 
  function()
  {
    alert('failed');
  } 
); 

If the protocol is not installed, the failure callback will fire. Otherwise, the protocol will launch and the success callback will fire.

I discuss this topic a bit further here: https://web.archive.org/web/20180308105244/https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/

This topic is of recent (2021) interest; see https://github.com/fingerprintjs/external-protocol-flooding for discussion.

Wendelina answered 15/9, 2012 at 18:26 Comment(3)
IT DOES NOT WORK - tested in Windows 7, IE browsersMend
msLaunchUri is Windows 8+ only.Wendelina
The link was dead.Friary
E
13

There seems to be no straightforward way via javascript to detect the presence of an installed app that has registered a protocol handler.

In the iTunes model, Apple provides urls to their servers, which then provide pages that run some javascript:

http://ax.itunes.apple.com/detection/itmsCheck.js

So the iTunes installer apparently deploys plugins for the major browsers, whose presence can then be detected.

If your plugin is installed, then you can be reasonably sure that redirecting to your app-specific url will succeed.

Excerpt answered 6/11, 2009 at 15:56 Comment(2)
This should be the most reliable solution. But I means that you need to install and also create a plugin for most of the browser and that is kind of tricky. I could be more userfriendly to be able to redirect the user to the download page instead.Lout
Why not using FireBreath as mentioned here? https://mcmap.net/q/225485/-how-to-check-if-a-custom-protocol-supportedAbnaki
L
11

What seams the most easy solution is to ask the user the first time.

Using a Javascript confirm dialog per example:

You need this software to be able to read this link. Did you install it ?

if yes: create a cookie to not ask next time; return false and the link applies
if false: window.location.href = '/downloadpage/'
Lout answered 24/1, 2010 at 8:34 Comment(3)
Cookies can be removed clean daily. Is there no better way you can make a Flash based cookie?Mend
Well if cookie get removed the user will just be prompted again.Lout
I think vscode use this method, check their site marketplace.visualstudio.comFriary
C
6

If you have control of the program you're trying to run (the code), one way to see if the user was successful in running the application would be to:

  1. Before trying to open the custom protocol, make an AJAX request to a server script that saves the user's intent in a database (for example, save the userid and what he wanted to do).

  2. Try to open the program, and pass on the intent data.

  3. Have the program make a request to the server to remove the database entry (using the intent data to find the correct row).

  4. Make the javascript poll the server for a while to see if the database entry is gone. If the entry is gone, you'll know the user was successful in opening the application, otherwise the entry will remain (you can remove it later with cronjob).

I have not tried this method, just thought it.

Carduaceous answered 26/4, 2012 at 7:47 Comment(0)
B
4

I was able to finally get a cross-browser (Chrome 32, Firefox 27, IE 11, Safari 6) solution working with a combination of this and a super-simple Safari extension. Much of this solution has been mentioned in one way or another in this and this other question.

Here's the script:

function launchCustomProtocol(elem, url, callback) {
    var iframe, myWindow, success = false;

    if (Browser.name === "Internet Explorer") {
        myWindow = window.open('', '', 'width=0,height=0');
        myWindow.document.write("<iframe src='" + url + "'></iframe>");

        setTimeout(function () {
            try {
                myWindow.location.href;
                success = true;
            } catch (ex) {
                console.log(ex);
            }

            if (success) {
                myWindow.setTimeout('window.close()', 100);
            } else {
                myWindow.close();
            }

            callback(success);
        }, 100);
    } else if (Browser.name === "Firefox") {
        try {
            iframe = $("<iframe />");
            iframe.css({"display": "none"});
            iframe.appendTo("body");
            iframe[0].contentWindow.location.href = url;

            success = true;
        } catch (ex) {
            success = false;
        }

        iframe.remove();

        callback(success);
    } else if (Browser.name === "Chrome") {
        elem.css({"outline": 0});
        elem.attr("tabindex", "1");
        elem.focus();

        elem.blur(function () {
            success = true;
            callback(true);  // true
        });

        location.href = url;

        setTimeout(function () {
            elem.off('blur');
            elem.removeAttr("tabindex");

            if (!success) {
                callback(false);  // false
            }
        }, 1000);
    } else if (Browser.name === "Safari") {
        if (myappinstalledflag) {
            location.href = url;
            success = true;
        } else {
            success = false;
        }

        callback(success);
    }
}

The Safari extension was easy to implement. It consisted of a single line of injection script:

myinject.js:

window.postMessage("myappinstalled", window.location.origin);

Then in the web page JavaScript, you need to first register the message event and set a flag if the message is received:

window.addEventListener('message', function (msg) {
    if (msg.data === "myappinstalled") {
        myappinstalledflag = true;
    }
}, false);

This assumes the application which is associated with the custom protocol will manage the installation of the Safari extension.

In all cases, if the callback returns false, you know to inform the user that the application (i.e., it's custom protocol) is not installed.

Bott answered 26/2, 2014 at 23:17 Comment(1)
It doesn't work for IE. How is it supposed to work? what's the connection between myWindow.location.href; and iframe src defined inside that window? Is it supposed to throw an exception? It doesn't regardless of whether the custom protocol is supported or not.Torchier
W
2

This was a recommended approach for IE by Microsoft support

http://msdn.microsoft.com/en-us/library/ms537503%28VS.85%29.aspx#related_topics

"If you have some control over the binaries being installed on a user’s machine, checking the UA in script seems like a relevant approach: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform " -- By M$ support

Every web page has access to the userAgent string and if you drop a custom post platform value, detecting this in javascript using navigator.userAgent is quite simple.

Fortunately, other major browsers like Firefox and Chrome (barring Safari :( ), do not throw "page not found" errors when a link with a custom protocol is clicked and the protocol is not installed on the users machine. IE is very unforgiving here, any trick to click in a invisible frame or trap javascript errors does not work and ends up with ugly "webpage cannot be displayed" error. The trick we use in our case is to inform users with browser specific images that clicking on the custom protocol link will open an application. And if they do not find the app opening up, they can click on an "install" page. In terms of XD this wprks way better than the ActiveX approach for IE. For FF and Chrome, just go ahead and launch the custom protocol without any detection. Let the user tell you what he sees. For Safari, :( no answers yet

Winker answered 6/12, 2010 at 8:53 Comment(1)
User-Agent extension is a common, but problematic, approach. blogs.msdn.com/b/ieinternals/archive/2009/10/08/…Wendelina
A
1

You say you need to detect the browser's protocol handlers - do you really?

What if you did something like what happens when you download a file from sourceforge? Let's say you want to open myapp://something. Instead of simply creating a link to it, create a link to another HTML page accessed via HTTP. Then, on that page, say that you're attempting to open the application for them. If it doesn't work, they need to install your application, which they can do by clicking on the link you'll provide. If it does work, then you're all set.

Article answered 28/9, 2009 at 6:12 Comment(1)
It's unhelpful to suggest that this wouldn't be useful. It seems obvious to me that being able to conditionally show a launch or download link - or even conditionally launch or download when clicking on a link would be a superior UX to telling the user they need to install first.Bibliofilm
A
1

I'm trying to do something similar and I just discovered a trick that works with Firefox. If you combine it with the trick for IE you can have one that works on both main browsers (I'm not sure if it works in Safari and I know it doesn't work in Chrome)

if (navigator.appName=="Microsoft Internet Explorer" && document.getElementById("testprotocollink").protocolLong=="Unknown Protocol") {
    alert("No handler registered");
} else {
    try {
        window.location = "custom://stuff";
    } catch(err) {
        if (err.toString().search("NS_ERROR_UNKNOWN_PROTOCOL") != -1) {
            alert("No handler registered");
        }
    }
}

In order for this to work you also need to have a hidden link somewhere on the page, like this:

<a id="testprotocollink" href="custom://testprotocol" style="display: none;">testprotocollink</a>

It's a bit hacky but it works. The Firefox version unfortunately still pops up the default alert that comes up when you try to visit a link with an unknown protocol, but it will run your code after the alert is dismissed.

Ambrose answered 29/1, 2010 at 20:20 Comment(2)
I found I still got the "Firefox doesn't know how to open this address, because the protocol (tel) isn't associated with any program." message before the catch blockSemiprofessional
protocolLong only returns results for "known" protocols (file:,mailto:,gopher:,ftp:,http:,https:,news:) and not for other Application Protocols.Wendelina
T
1

You can try something like this:

function OpenCustomLink(link) {

    var w = window.open(link, 'xyz', 'status=0,toolbar=0,menubar=0,height=0,width=0,top=-10,left=-10');
    if(w == null) {            
        //Work Fine
    }
    else {
        w.close();
        if (confirm('You Need a Custom Program. Do you want to install?')) {
            window.location = 'SetupCustomProtocol.exe'; //URL for installer
        }
    }
}
Tetartohedral answered 10/5, 2010 at 20:30 Comment(2)
No, does not work in Firefox 27. (Didn't test in other browsers)Criswell
DOES NOT WORK - tested in Windows 7, IE, Firefox, Opera, ChromeMend
D
0

This is not a trivial task; one option might be to use signed code, which you could leverage to access the registry and/or filesystem (please note that this is a very expensive option). There is also no unified API or specification for code signing, so you would be required to generate specific code for each target browser. A support nightmare.

Also, I know that Steam, the gaming content delivery system, doesn't seem to have this problem solved either.

Dasha answered 8/5, 2009 at 2:0 Comment(1)
The signed code should be signed by a certificate that is installed along with the application that handles the custom:// so there'd be no need for a really expensive application certificate signed by someone like verisign.Loblolly
P
0

Here's another hacky answer that would require (hopefully light) modification to your application to 'phone home' on launch.

  1. User clicks link, which attempts to launch the application. A unique identifier is put in the link, so that it's passed to the application when it launches. Web app shows a spinner or something of that nature.
  2. Web page then starts checking for a 'application phone home' event from an app with this same unique ID.
  3. When launched, your application does an HTTP post to your web app with the unique identifier, to indicate presence.
  4. Either the web page sees that the application launched, eventually, or moves on with a 'please download' page.
Parental answered 27/3, 2014 at 21:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.