【问题标题】:Check whether user has a Chrome extension installed检查用户是否安装了 Chrome 扩展程序
【发布时间】:2011-09-11 17:14:08
【问题描述】:

我正在构建一个 Chrome 扩展程序,为了让整个事情按我希望的方式工作,我需要一个外部 JavaScript 脚本来检测用户是否安装了我的扩展程序。

例如:用户安装了我的插件,然后访问了一个带有我的脚本的网站。该网站检测到我的扩展程序已安装并相应地更新页面。

这可能吗?

【问题讨论】:

标签: javascript google-chrome google-chrome-extension


【解决方案1】:

Chrome 现在可以将消息从网站发送到扩展程序。

所以在扩展 background.js(content.js 不起作用)中添加如下内容:

chrome.runtime.onMessageExternal.addListener(
    function(request, sender, sendResponse) {
        if (request) {
            if (request.message) {
                if (request.message == "version") {
                    sendResponse({version: 1.0});
                }
            }
        }
        return true;
    });

这将让您从网站拨打电话:

var hasExtension = false;

chrome.runtime.sendMessage(extensionId, { message: "version" },
    function (reply) {
        if (reply) {
            if (reply.version) {
                if (reply.version >= requiredVersion) {
                    hasExtension = true;
                }
            }
        }
        else {
          hasExtension = false;
        }
    });

然后您可以检查 hasExtension 变量。唯一的缺点是调用是异步的,所以你必须以某种方式解决这个问题。

编辑: 如下所述,您需要向 manifest.json 添加一个条目,其中列出了可以向您的插件发送消息的域。例如:

"externally_connectable": {
    "matches": ["*://localhost/*", "*://your.domain.com/*"]
},

2021 年更新: chrome.runtime.sendMessage 如果未安装扩展程序或已禁用扩展程序,将在控制台中抛出以下异常。

未经检查的 runtime.lastError:无法建立连接。接收端不存在

要解决此问题,请在 sendMessage 回调中添加此验证

if (chrome.runtime.lastError) {
    // handle error 
}

【讨论】:

  • 这就像一个魅力。另一个缺点当然是您必须控制扩展 - 您不能使用它来查看是否安装了任意第三方扩展。
  • @EricP 最初的问题是他们正在编写扩展,所以这个问题没有实际意义。
  • 您还必须在 manifest.json 中添加以下内容:“externally_connectable”:{“matches”:[“://.yourdomain.com/*” ] }
  • 它应该是 {version: '1.0'} 而不是 {version: 1.0} 否则你会在扩展检查视图控制台中得到'Uncaught SyntaxError: Unexpected number'。
  • 在“检查”方面(网页试图检查给定扩展的可用性),chrome.runtime 未定义,linux 上的 chrome 36。
【解决方案2】:

您可以通过以下方式检测安装的特定扩展程序并显示警告消息。

首先,您需要通过 chrome-extension://extension_id_here_hkdppipefbchgpohn/manifest.json 打开扩展的清单文件,并在“web_accessible_resources”部分中查找任何文件名。

<div class="chromewarning" style="display:none">
    <script type="text/javascript">
                            $.get("chrome-extension://extension_id_here_hkdppipefbchgpohn/filename_found_in_ web_accessible_resources.png").done(function () {
                              $(".chromewarning").show();
                            }).fail(function () {
                             //  alert("failed.");
                            });
                        </script>
                        <p>We have detected a browser extension that conflicts with learning modules in this course.</p>
            </div>

【讨论】:

    【解决方案3】:

    这是另一种现代方法:

    const checkExtension = (id, src, callback) => {
        let e = new Image()
        e.src = 'chrome-extension://'+ id +'/'+ src
        e.onload = () => callback(1), e.onerror = () => callback(0)
    }
    
    // "src" must be included to "web_accessible_resources" in manifest.json
    checkExtension('gighmmpiobklfepjocnamgkkbiglidom', 'icons/icon24.png', (ok) => {
        console.log('AdBlock: %s', ok ? 'installed' : 'not installed')
    })
    checkExtension('bhlhnicpbhignbdhedgjhgdocnmhomnp', 'images/checkmark-icon.png', (ok) => {
        console.log('ColorZilla: %s', ok ? 'installed' : 'not installed')
    })
    

    【讨论】:

    • 确保更新清单以允许访问资源:"web_accessible_resources": ["icons/*.png"]
    • 这是一个很棒的解决方案 - 完美运行!
    【解决方案4】:

    我想我会分享我对此的研究。 我需要能够检测是否为某些 file:/// 链接安装了特定的扩展程序才能正常工作。 我偶然发现了这篇文章here 这解释了一种获取扩展的 manifest.json 的方法。

    我稍微调整了代码并想出了:

    function Ext_Detect_NotInstalled(ExtName, ExtID) {
      console.log(ExtName + ' Not Installed');
      if (divAnnounce.innerHTML != '')
        divAnnounce.innerHTML = divAnnounce.innerHTML + "<BR>"
    
      divAnnounce.innerHTML = divAnnounce.innerHTML + 'Page needs ' + ExtName + ' Extension -- to intall the LocalLinks extension click <a href="https://chrome.google.com/webstore/detail/locallinks/' + ExtID + '">here</a>';
    }
    
    function Ext_Detect_Installed(ExtName, ExtID) {
      console.log(ExtName + ' Installed');
    }
    
    var Ext_Detect = function (ExtName, ExtID) {
      var s = document.createElement('script');
      s.onload = function () { Ext_Detect_Installed(ExtName, ExtID); };
      s.onerror = function () { Ext_Detect_NotInstalled(ExtName, ExtID); };
      s.src = 'chrome-extension://' + ExtID + '/manifest.json';
      document.body.appendChild(s);
    }
    
    var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
    
    if (is_chrome == true) {
      window.onload = function () { Ext_Detect('LocalLinks', 'jllpkdkcdjndhggodimiphkghogcpida'); };
    }
    

    有了这个,您应该能够使用 Ext_Detect(ExtensionName,ExtensionID) 来检测任意数量的扩展程序的安装。

    【讨论】:

    • 看起来谷歌让事情变得更安全,当我运行 Ext_Detect() 时出现以下错误:拒绝加载 chrome-extension://[my_extension_id]/manifest.json。资源必须列在 web_accessible_resources 清单键中,才能被扩展程序之外的页面加载。
    • 截至 2014 年 2 月 27 日,我可以让它与 Chrome 的 32.0.1700.107 m 版本一起使用
    • 正如@Lounge9 所说。使用 manifest_version 2(或更高版本)的包内的资源是 blocked by default,并且必须是 whitelisted 才能通过此属性添加到 manifest.json: "web_accessible_resources": [ "manifest..json" ],
    • 使用@BJury 回答,您还可以轻松地将数据从扩展程序传递到脚本(例如扩展程序版本),并且您不需要从扩展程序公开任何文件。
    • 这对我来说效果最好,因为我们的扩展将在多个域中使用,并且由于定期添加新域而无法预定义。我没有访问 manifest.json,而是创建了一个新文件 version.json 并将版本号放入其中。这同样有效。
    【解决方案5】:

    如果您尝试检测来自任何网站的任何扩展程序, 这篇文章有帮助:https://ide.hey.network/post/5c3b6c7aa7af38479accc0c7

    基本上,解决方案是简单地尝试通过指定路径从扩展中获取特定文件(manifest.json 或图像)。这是我用的。绝对有效:

    const imgExists = function(_f, _cb) {
        const __i = new Image();
        __i.onload = function() {
            if (typeof _cb === 'function') {
                _cb(true);
            }
        }
        __i.onerror = function() {
            if (typeof _cb === 'function') {
                _cb(false);
            }
        }
        __i.src = _f;
        __i = null;
    });
    
    try {
        imgExists("chrome-extension://${CHROME_XT_ID}/xt_content/assets/logo.png", function(_test) {
            console.log(_test ? 'chrome extension installed !' : 'chrome extension not installed..');
            ifrm.xt_chrome = _test;
            // use that information
        });
    } catch (e) {
        console.log('ERROR', e)
    }
    

    【讨论】:

      【解决方案6】:

      到目前为止,这里的很多答案都只是 Chrome,否则会导致 HTTP 开销损失。我们使用的解决方案有点不同:

      1。像这样向清单 content_scripts 列表添加一个新对象:

      {
        "matches": ["https://www.yoursite.com/*"],
        "js": [
          "install_notifier.js"
        ],
        "run_at": "document_idle"
      }
      

      这将允许 install_notifier.js 中的代码在该站点上运行(如果您在那里没有权限)。

      2。向上面清单键中的每个站点发送消息。

      在 install_notifier.js 中添加类似的内容(注意,这是使用闭包来防止变量成为全局变量,但这不是绝对必要的):

      // Dispatch a message to every URL that's in the manifest to say that the extension is
      // installed.  This allows webpages to take action based on the presence of the
      // extension and its version. This is only allowed for a small whitelist of
      // domains defined in the manifest.
      (function () {
        let currentVersion = chrome.runtime.getManifest().version;
        window.postMessage({
          sender: "my-extension",
          message_name: "version",
          message: currentVersion
        }, "*");
      })();
      

      您的消息可以说任何内容,但发送版本很有用,这样您就知道自己在处理什么。那么……

      3。在您的网站上收听该消息。

      将此添加到您的网站某处:

      window.addEventListener("message", function (event) {
        if (event.source == window &&
          event.data.sender &&
          event.data.sender === "my-extension" &&
          event.data.message_name &&
          event.data.message_name === "version") {
          console.log("Got the message");
        }
      });
      

      这适用于 Firefox 和 Chrome,不会产生 HTTP 开销或操作页面。

      【讨论】:

        【解决方案7】:

        另一种方法是expose a web-accessible resource,尽管这将允许任何网站测试您的扩展程序是否已安装。

        假设您的扩展程序的 ID 是 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,并且您在扩展程序的文件中添加了一个文件(例如,透明像素图像)为 test.png

        然后,使用 web_accessible_resources 清单键将此文件公开给网页:

          "web_accessible_resources": [
            "test.png"
          ],
        

        在您的网页中,您可以尝试通过其完整 URL 加载此文件(在 &lt;img&gt; 标记中,通过 XHR 或以任何其他方式):

        chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/test.png
        

        如果文件加载,则安装扩展。如果加载此文件时出错,则说明未安装扩展。

        // Code from https://groups.google.com/a/chromium.org/d/msg/chromium-extensions/8ArcsWMBaM4/2GKwVOZm1qMJ
        function detectExtension(extensionId, callback) { 
          var img; 
          img = new Image(); 
          img.src = "chrome-extension://" + extensionId + "/test.png"; 
          img.onload = function() { 
            callback(true); 
          }; 
          img.onerror = function() { 
            callback(false); 
          };
        }
        

        注意:如果在加载此文件时出现错误,则 network stack 错误将出现在控制台中,无法将其静音。 Chromecast 使用这种方法时,它caused quite a bit of controversy 是因为这个;最终非常丑陋的解决方案是由 Chrome 团队完全从开发工具中将非常具体的错误列入黑名单


        重要提示:此方法不适用于 Firefox WebExtensions。 Web 可访问资源固有地将扩展暴露给指纹,因为 URL 可以通过知道 ID 来预测。 Firefox 决定通过assigning an instance-specific random URL 将这个漏洞堵在网络可访问的资源上:

        这些文件将通过如下 URL 提供:

        moz-extension://<random-UUID>/<path/to/resource>
        

        此 UUID 是为每个浏览器实例随机生成的,不是您的扩展程序的 ID。这可以防止网站对用户安装的扩展程序进行指纹识别。

        但是,虽然扩展程序可以使用runtime.getURL() 来获取此地址,但您不能在您的网站中对其进行硬编码。

        【讨论】:

        • 虽然这个答案从stackoverflow.com/a/9216924/1504300 获得了“果汁”,但恕我直言,添加了一些非常重要的信息,例如在扩展中公开资源以及您可以使用 ajax 请求来检查存在的事实(如果我没记错的话,图像对象似乎只在 HTML5 上可用goo.gl/HBeI1i)。有了这个答案中的信息,我已经能够解决问题,我发现它就像一个“开箱即用”的解决方案
        • @niconic 这个答案(无论如何都不好作为仅链接)是指清单版本 2 生效之前的情况。以前,不需要声明资源可通过网络访问。
        • 网页不允许我们访问chrome-extension://
        【解决方案8】:

        您也可以使用我使用过的跨浏览器方法。 使用添加 div 的概念。

        在您的内容脚本中(无论何时加载脚本,它都应该这样做)

        if ((window.location.href).includes('*myurl/urlregex*')) {
                $('html').addClass('ifextension');
                }
        

        在您的网站中,您断言类似,

        if (!($('html').hasClass('ifextension')){}
        

        并抛出适当的消息。

        【讨论】:

        • 我已经对此进行了测试,但操作顺序似乎很奇怪——哪个脚本首先运行等等?
        • @BradyMoritz 与答案中的顺序相同。先添加类,再断言。
        • 我的内容脚本似乎不一定在我的页内脚本之前运行?
        • 那个,我认为很容易解决。您可以确保您的内容脚本在使用正则表达式点击所需的 URL/路由后立即运行。您尝试过吗?
        【解决方案9】:

        您的扩展程序可以与网站交互(例如更改变量)并且您的网站可以检测到这一点。

        但是应该有更好的方法来做到这一点。我想知道谷歌在他们的扩展库上是如何做到的(已经安装的应用程序被标记)。

        编辑:

        图库使用chrome.management.get 函数。示例:

        chrome.management.get("mblbciejcodpealifnhfjbdlkedplodp", function(a){console.log(a);});
        

        但您只能从具有正确权限的页面访问该方法。

        【讨论】:

        • 是的,这需要扩展程序与每个选项卡上的每个站点进行交互,这将是缓慢/难以实施和错误的:-/
        • 问题在于,由于 chrome 的安全模型,无法在另一个方向(页面到扩展名)进行通信。如果你不想走“互动”的方式,那就走 cookie 的方式。
        • 亲爱的@Fox32,chrome.management.get...,返回此错误:Uncaught TypeError: Cannot read property 'get' of undefined
        【解决方案10】:

        网页通过后台脚本与扩展交互。

        manifest.json:

        "background": {
            "scripts": ["background.js"],
            "persistent": true
        },
        "externally_connectable": {
            "matches": ["*://(domain.ext)/*"]
        },
        
        background.js:
        chrome.runtime.onMessageExternal.addListener(function(msg, sender, sendResponse) {
            if ((msg.action == "id") && (msg.value == id))
            {
                sendResponse({id : id});
            }
        });
        

        page.html:

        <script>
        var id = "some_ext_id";
        chrome.runtime.sendMessage(id, {action: "id", value : id}, function(response) {
            if(response && (response.id == id)) //extension installed
            {
                console.log(response);
            }
            else //extension not installed
            {
                console.log("Please consider installig extension");
            }
        
        });
        </script>
        

        【讨论】:

        • 不适用于不支持 externally_connectable 的 Firefox WebExtensions。
        【解决方案11】:

        如果你可以控制 Chrome 扩展,你可以试试我做的:

        // Inside Chrome extension
        var div = document.createElement('div');
        div.setAttribute('id', 'myapp-extension-installed-div');
        document.getElementsByTagName('body')[0].appendChild(div);
        

        然后:

        // On web page that needs to detect extension
        if ($('#myapp-extension-installed-div').length) {
        
        }
        

        感觉有点hacky,但我无法让其他方法工作,我担心Chrome会在这里改变它的API。怀疑这种方法是否会很快停止工作。

        【讨论】:

        • 如上所述-我已经对此进行了测试,但操作顺序似乎很奇怪-哪个脚本首先运行等等?
        【解决方案12】:

        我用的是cookie方法:

        在我的 manifest.js 文件中,我包含了一个仅在我的网站上运行的内容脚本:

         "content_scripts": [
                {
                "matches": [
                    "*://*.mysite.co/*"
                    ],
                "js": ["js/mysite.js"],
                "run_at": "document_idle"
                }
            ], 
        

        在我的 js/mysite.js 中有一行:

        document.cookie = "extension_downloaded=True";
        

        然后在我的 index.html 页面中寻找那个 cookie。

        if (document.cookie.indexOf('extension_downloaded') != -1){
            document.getElementById('install-btn').style.display = 'none';
        }
        

        【讨论】:

        • 我尝试了上面的所有解决方案但都不起作用,然后我看到了你的答案,这就是我要找的!
        • 这会增加以后每个 HTTP 请求的开销。
        【解决方案13】:

        我确信有一种直接的方法(直接调用你的扩展上的函数,或者使用 JS 类进行扩展),但有一种间接的方法(直到出现更好的方法):

        让您的 Chrome 扩展程序在您的页面上查找具有非常特定 ID 的特定 DIV 或其他元素。

        例如:

        <div id="ExtensionCheck_JamesEggersAwesomeExtension"></div>
        

        执行getElementById 并将innerHTML 设置为您的扩展程序的版本号或其他内容。然后您可以读取该客户端的内容。

        同样,如果有可用的方法,您应该使用直接方法。


        编辑:找到直接方法!!

        使用此处找到的连接方法:https://developer.chrome.com/extensions/extension#global-events

        未经测试,但你应该能够做到...

        var myPort=chrome.extension.connect('yourextensionid_qwerqweroijwefoijwef', some_object_to_send_on_connect);
        

        【讨论】:

        • hmmm chrome.extension.connect 似乎仅在从扩展(或任何扩展)中执行时才有效。我需要它从任何随机的 js 脚本中工作。有什么想法吗?
        • 奇怪,文档说它应该可以工作。 “与其他 chrome.* API 不同,内容脚本可以使用 chrome.extension 的一部分”,它列出了 sendRequest()onRequestconnect()onRequestgetURL()
        • @James 您是否正在从托管脚本执行使用 .connect() 的脚本?我知道 Chrome 努力不只使用出于安全目的未托管的本地文件。 - 只是检查。
        • @James 我正在执行 .connect() 的脚本在同一台服务器上,如果这就是你的意思吗?
        • 最后一个方法不再有效,因为 connect 函数已移至 chrome.runtime 命名空间。请参阅 BJury 的答案(和 cmets)以获取更新的版本
        【解决方案14】:

        this Google Groups post 显示了另一种方法。简而言之,您可以尝试检测扩展图标是否加载成功。如果您要检查的扩展程序不是您自己的,这可能会有所帮助。

        【讨论】:

        • 好的。我们如何检查扩展图标是否存在?
        【解决方案15】:

        如果您拥有该网站,另一种可能的解决方案是使用inline installation

        if (chrome.app.isInstalled) {
          // extension is installed.
        }
        

        我知道这是一个老问题,但这种方式是在 Chrome 15 中引入的,所以我认为我只为现​​在寻找答案的人列出它。

        【讨论】:

        • 这适用于 Chrome App,但不适用于 Chrome 扩展 AFAIK
        • 是的,该网站确实告诉您如何内联 install 扩展程序,但显然建议“扩展程序可以通过内容脚本与嵌入页面通信,让它知道它们已经已安装。”而不是能够使用 chrome.app.isInstalled。我也很困惑……
        【解决方案16】:

        您可以使用扩展名set a cookie 并让您的网站 JavaScript 检查该 cookie 是否存在并相应更新。这个方法以及这里提到的大多数其他方法当然可以被用户规避,除非您尝试让扩展程序根据时间戳等创建自定义 cookie,并让您的应用程序在服务器端分析它们以查看是否确实是拥有该扩展程序的用户,或者是通过修改其 cookie 来假装拥有该扩展程序的用户。

        【讨论】:

        • 唯一的问题是,如果用户删除了您的扩展程序。 cookie 可能会保持设置状态。
        猜你喜欢
        • 2017-02-15
        • 2012-12-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多