如何检查是否支持自定义协议

时间:2010-05-20 08:28:18

标签: javascript

我们正在使用注册自己协议的软件。我们可以从浏览器运行应用程序,然后通过以下链接运行:

customprotocol://do_this.

但有没有办法检查用户系统支持的自定义协议?如果不是,我们首先要求用户安装软件。

E.g:

if (canHandle ('customprotocol')) {
     // run software
}
else {
    // ask to install
}

修改 我知道protocolLong属性,但它只适用于IE。

8 个答案:

答案 0 :(得分:45)

不幸的是,没有简单的方法来实现这一目标。当然没有预先确定是否安装协议处理程序的方法。

正如您所提到的,

Internet Explorer 具有protocolLong属性,但我无法为所有自定义协议处理程序返回除“未知协议”以外的任何内容 - 如果谁知道如何让IE返回正确的值请让我知道所以我可以更新这一节。我在IE中找到的最佳解决方案是append to the user agent string或安装浏览器扩展程序以及展示Javascript可访问属性的应用程序。

Firefox 是目前最简单的主流浏览器,因为它可以让您尝试捕获失败的导航尝试。返回的错误对象包含name属性,其值为NS_ERROR_UNKNOWN_PROTOCOL

try {
    iframe.contentWindow.location.href = "randomprotocolstring://test/";
} catch(e) {
    if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL")
        window.location = "/download/";
}

Firefox将弹出自己的警告框:

  

Firefox不知道如何打开此地址,因为协议(randomprotocolstring)与任何程序都没有关联。

关闭此框后,catch块将会执行,并且您有一个工作后备。

其次是 Opera ,它允许您使用可预测性法则来检测单击的自定义协议链接是否成功。如果自定义协议单击有效,则页面将保持相同的位置。如果没有安装处理程序,Opera将导航到错误页面。这使得使用iframe检测非常容易:

   iframe.contentWindow.location = "randomprotocolstring://test/";
   window.setTimeout(function () {
       try {
           alert(ifr.contentWindow.location); 
       } catch (e) { window.location = "/download/"; }
   }, 0);

这里的setTimeout是为了确保我们在导航后检查位置。重要的是要注意,如果您尝试访问该页面,Opera会抛出ReferenceException(跨域安全性错误)。这没关系,因为我们需要知道的是,位置已从about:blank更改,因此try...catch工作正常。

Chrome 在这方面正式糟透了。如果自定义协议处理程序失败,它绝对会拉链。如果处理程序有效......你猜对了......它绝对是拉链的。我不敢区分这两者。

我还没有测试 Safari ,但我担心它会和Chrome一样。

欢迎您在调查时尝试test code I wrote(我自己对此感兴趣)。它与Opera和Firefox交叉兼容,但目前在IE和Chrome中无效。

答案 1 :(得分:11)

为了了解我们自己的经验,我们使用FireBreath创建了一个简单的跨平台插件。安装完成后,此插件会注册一个mime类型,可以在页面刷新后从浏览器javascript中检测到该类型。检测mime类型表示已安装协议处理程序。

if(IE) { //This bastard always needs special treatment
    try {
        var flash = new ActiveXObject("Plugin.Name");
    } catch (e) {
        //not installed
    }
else { //firefox,chrome,opera
    navigator.plugins.refresh(true);
    var mimeTypes = navigator.mimeTypes;
    var mime = navigator.mimeTypes['application/x-plugin-name'];
    if(mime) {
        //installed
    } else {
        //not installed
    }
}

答案 2 :(得分:7)

Windows 8上的Internet Explorer 10引入了非常有用的navigator.msLaunchUri方法,用于启动自定义协议URL并检测成功或失败。例如:

        if (typeof (navigator.msLaunchUri) == typeof (Function)) {
            navigator.msLaunchUri(witchUrl,
                function () { /* Success */ },
                function () { /* Failure */ showError(); });

            return;
        }

Windows 7 / IE 9及更低版本支持@ mark-kahn建议的条件评论。

答案 3 :(得分:5)

这是一个非常简单的答案:在注册自定义协议时安装一个不寻常的字体。然后使用javascript来检查该字体是否存在,使用like this

确定它是一个黑客,但与其他答案不同,它可以在浏览器和操作系统上运行。

答案 4 :(得分:4)

对于Internet Explorer,我发现的最佳解决方案是使用Conditionl评论&版本向量(应用程序必须在安装协议时向注册表写入内容,请参阅http://msdn.microsoft.com/en-us/library/ms537512.aspx#Version_Vectors)。 protocolLong不适用于自定义协议。

答案 5 :(得分:3)

在移动设备上,您可以使用嵌入式iframe在自定义协议和已知协议(网络或应用商店)之间自动切换,请参阅https://gist.github.com/2662899

答案 6 :(得分:2)

我只想解释Mark先前的答案(有些人不理解,例如user7892745)。

1)当您启动网页或网络应用程序时,它会检查一个不寻常的字体(类似于中文Konfuciuz字体http://www.fontspace.com/apostrophic-lab/konfuciuz)。

下面是带有检查字体功能的示例网页代码(名为isFontAvailable):

<!DOCTYPE html>
<html>
<head>

</head>
<body>

<script>
/**
* Checks if a font is available to be used on a web page.
*
* @param {String} fontName The name of the font to check
* @return {Boolean}
* @license MIT
* @copyright Sam Clarke 2013
* @author Sam Clarke <sam@samclarke.com>
*/
(function (document) {
   var width;
   var body = document.body;

                    var container = document.createElement('span');
                    container.innerHTML = Array(100).join('wi');
                    container.style.cssText = [
       'position:absolute',
       'width:auto',
       'font-size:128px',
       'left:-99999px'
   ].join(' !important;');

   var getWidth = function (fontFamily) {
       container.style.fontFamily = fontFamily;

       body.appendChild(container);
       width = container.clientWidth;
       body.removeChild(container);

       return width;
   };

   // Pre compute the widths of monospace, serif & sans-serif
   // to improve performance.
   var monoWidth  = getWidth('monospace');
   var serifWidth = getWidth('serif');
   var sansWidth  = getWidth('sans-serif');

   window.isFontAvailable = function (font) {
       return monoWidth !== getWidth(font + ',monospace') ||
           sansWidth !== getWidth(font + ',sans-serif') ||
           serifWidth !== getWidth(font + ',serif');
   };
})(document);



function isProtocolAvailable()
{
    if (isFontAvailable('Konfuciuz')) 
    {
        return true;
    } 
    else 
    {
        return false;
    }
}

function checkProtocolAvail()
{
    if (isProtocolAvailable()) 
    {
        alert('Custom protocol is available!');
    } 
    else 
    {
        alert('Please run executable to install protocol!');
    }
}
</script>

<h3>Check if custom protocol was installed or not</h3>

<pre>


<input type="button" value="Check if custom protocol was installed!" onclick="checkProtocolAvail()">

</body>
</html>

2)用户第一次打开此页面时,将不会安装字体,因此他会收到一条消息,说明&#34;请运行可执行文件以安装自定义协议...&#34;。

3)他将运行将安装该字体的可执行文件。您的exe只需将字体文件(在我的情况下是KONFUC __。ttf)复制到C:\ Windows目录或使用这样的代码(Delphi上的示例):

// Adding the font ..

AddFontResource(PChar('XXXFont.TTF'));
SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);

4)之后,当用户再次运行Web应用程序时,他会获得“#34;自定义协议可用!”#34;消息,因为这次安装了字体。

在Google Chrome,Internet Explorer和Firefox上测试过 - 效果很好!

答案 7 :(得分:0)

关于Firefox,我搜索了大部分文章,包括 Andy E 在这里的答案,以及要点Cross-browser implementation of navigator.msLaunchUrihttps://github.com/ismailhabib/custom-protocol-detection使用

iframe.contentWindow.location.href = uri

但是自Firefox 64起,它就停止了工作,例如此处https://github.com/ismailhabib/custom-protocol-detection/issues/37也证实了这一点。

所以FF 64+我发现我可以使用Chrome的方法blurHandler或使用https://github.com/ismailhabib/custom-protocol-detection/issues/37#issuecomment-617962659上的帖子

try {
        iframe.contentWindow.location.href = uri;
        setTimeout(function () {
             try {
                    if (iframe.contentWindow.location.protocol === "about:") {
                         successCb();
                     } else {
                         failCb();
                     }
             } catch (e) {
                if (e.name === "NS_ERROR_UNKNOWN_PROTOCOL" || 
                 e.name === "NS_ERROR_FAILURE" || e.name === "SecurityError") {
                     failCb();
               }
             }
        }, 500);
} catch (e) {
   if (e.name === "NS_ERROR_UNKNOWN_PROTOCOL" || e.name === "NS_ERROR_FAILURE" 
    || e.name === "SecurityError") {
       failCb();
   }
}