检查用户是否已安装Chrome扩展程序


97

我正在构建Chrome扩展程序,为了使整个工作正常进行,我需要一个外部JavaScript脚本来检测用户是否安装了我的扩展程序。

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

这可能吗?


2
是的,只要您知道自己的扩展程序ID(我确定您可以),就可以检测扩展程序。请访问此网站了解更多信息:blog.kotowicz.net/2012/02/intro-to-chrome-addons-hacking.html跳至“逐个查找插件”一节。祝好运!
马丁·休斯

BJury bellow描述了实现此目标的正确方法。
拉赫塔尔

Answers:


46

我敢肯定,有一种直接方法(直接在扩展程序上调用函数,或通过使用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);

2
hmmm chrome.extension.connect仅在从扩展名(或任何扩展名)中执行时才起作用。我需要它从任何随机js脚本中运行。有任何想法吗?

奇怪的是,文档说它应该可以工作。“不像其他的铬。*的API,chrome.extension的部分可以由内容脚本中使用”,它列出sendRequest()onRequestconnect()onRequest,和getURL()
Brad

@James是否正在执行从托管脚本中使用.connect()的脚本?我知道Chrome浏览器会尽力不对未出于安全目的而托管的本地文件进行处理。- 只是检查。
JamesEggers 2011年

如果您的意思是@James,我正在执行.connect()的脚本位于同一服务器上?

23
最后一个方法不再有效,因为connect函数已移至chrome.runtime名称空间。有关最新版本,请参阅BJury的答案(和评论)
Xan

117

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/*"]
},

2
这就像一个魅力。当然,另一个缺点是您必须控制扩展-您不能使用它来查看是否安装了任意第三方扩展。
Eric P

2
@EricP最初的问题是他们正在编写扩展程序,因此该问题尚无定论。
BJury 2014年

13
您还必须将以下内容添加到manifest.json:“ externally_connectable”:{“ matches”:[“ :// .yourdomain.com / *”]}
noname

3
它应该是{version:'1.0'}而不是{version:1.0},否则您将在扩展名Inspect视图控制台中收到“ Uncaught SyntaxError:Unexpected number”。
ET-CS

1
在“检查”方面(试图检查给定扩展名可用性的网页),chrome.runtime是未定义的,在Linux上是chrome 36。
realnice 2015年

22

另一种方法是公开可访问Web的资源,尽管这将允许任何网站测试是否安装了扩展程序。

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

然后,使用web_accessible_resources清单密钥将此文件公开到网页:

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

在您的网页中,您可以尝试通过文件的完整URL加载该文件(在<img>标签中,通过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); 
  };
}

值得注意的是:如果在加载此文件时出现错误,则该网络堆栈错误将出现在控制台中,无法使其静音。Chromecast使用此方法时,因此引起了很多争议;最终的解决方案非常丑陋,Chrome团队将所有来自Dev Tools的特定错误完全列入了黑名单


重要说明:此方法在Firefox WebExtensions中不起作用。可通过Web访问的资源固有地将扩展暴露给指纹识别,因为URL可以通过知道ID来预测。Firefox决定通过为网络可访问资源分配实例特定的随机URL来弥补这一漏洞:

这些文件将通过以下网址提供:

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

该UUID是为每个浏览器实例随机生成的,不是您扩展程序的ID。这样可以防止网站为用户安装的扩展程序添加指纹。

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


尽管此答案从stackoverflow.com/a/9216924/1504300中获取了“汁液” ,但IMHO还是添加了一些非常重要的信息,例如在扩展中公开资源以及您可以使用ajax请求检查存在性的事实(Image对象)。如果我没看错的话,似乎仅在HTML5上可用goo.gl/HBeI1i)。有了此答案中的信息,我已经能够解决问题,我发现它就像是“开箱即用”的解决方案
真的很不错,2015年

@niconic该答案(无论如何也仅是链接,还是很糟糕的)指的是清单版本2生效之前的情况。以前,无需声明资源可通过网络访问。
Xan 2015年

19

我以为我会分享我对此的研究。我需要能够检测是否为某些file:///链接安装了特定的扩展名才能正常工作。我在这里碰到了这篇文章 这解释了一种获取扩展的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)来检测任意数量的扩展的安装。


2
看起来Google使事情变得更加安全,运行Ext_Detect()时出现以下错误:拒绝加载chrome-extension:// [my_extension_id] /manifest.json。必须在web_accessible_resources清单密钥中列出资源,以便由扩展之外的页面加载。
休息室

截至2014
JE Carter II

1
正如@ Lounge9所说。默认情况下,使用manifest_version 2(或更高版本)的软件包内部的资源被阻止,并且必须通过添加到manifest.json中而列入白名单以通过此属性使用:“ web_accessible_resources”:[“ manifest..json”],
ET-CS

使用@BJury答案,您还可以轻松地将数据从扩展名传递到脚本(例如,扩展名版本),并且无需公开扩展名中的任何文件。
ET-CS

1
这对我来说最有效,因为我们的扩展程序将在多个域中使用,并且由于要定期添加新域而无法预先定义。我没有访问manifest.json,而是创建了一个新文件version.json并将版本号放入其中。这是一样的。
Paul Haggo,2015年

7

如果您拥有网站,另一种可能的解决方案是使用嵌入式安装

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

我知道这是一个古老的问题,但是Chrome 15引入了这种方式,因此我认为Id仅将其列出给现在正在寻找答案的人。


12
这非常适合Chrome 应用,但不适用于Chrome扩展AFAIK
Eran Medan

是的,该网站确实告诉您如何内联安装扩展程序,但是显然建议“扩展程序可以通过内容脚本与嵌入页面进行通信,以告知扩展程序已被安装。” 而不是能够使用chrome.app.isInstalled。我也感到困惑...
rogerdpack


4

我使用了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请求的开销。
mlissner '18

3

您可以让扩展程序设置一个cookie,并让您的网站JavaScript检查该cookie是否存在并相应地进行更新。除非您尝试让扩展程序根据时间戳等创建自定义Cookie,并让您的应用程序在服务器端对其进行分析,以查看它是否真的是用户,否则,用户可能当然会忽略这里提到的这种方法以及大多数其他方法。扩展名或通过修改Cookie假装拥有该扩展名的人。


5
唯一的问题是,如果用户删除您的扩展程序。Cookie可能会保持设置状态。
Chase Roberts'3


3

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

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>

在不支持external_connectable的Firefox WebExtensions上不起作用。
mlissner '18

3

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

但是应该有一个更好的方法来做到这一点。我不知道Google如何在其扩展程序库(已标记已安装的应用程序)上做到这一点。

编辑:

画廊使用chrome.management.get函数。例:

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

但是您只能从具有适当权限的页面访问该方法。


1
是的,这需要扩展程序才能与每个选项卡上的每个站点进行交互,这将使实施缓慢/困难并且

问题是,由于chrome的安全模型,无法进行其他方向的通信(从扩展到页面)。如果您不想采用“交互”方式,请采用Cookie方式。
Fox32 2011年

2
尊敬的@ Fox32,chrome.management.get ...,返回此错误:Uncaught TypeError: Cannot read property 'get' of undefined
Hosein Aqajani 2015年

3

到目前为止,这里的许多答案仅是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开销或操纵页面。


0

如果您可以控制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) {

}

感觉有些棘手,但我无法使用其他方法,因此我担心Chrome在这里更改其API。令人怀疑的是,这种方法将很快停止工作。


如上所述-我已经测试过了,但是操作顺序似乎很奇怪-哪个脚本先运行等等?
布雷迪·莫里茨

0

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

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

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

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

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

并抛出适当的消息。


我已经对此进行了测试,但是操作顺序似乎很奇怪-首先运行哪个脚本等?
布雷迪·莫里茨

@BradyMoritz与答案中的顺序相同。首先添加类,然后断言。
Prakash Palnati

看来我的内容脚本不一定在页面内脚本之前运行?
布雷迪·莫里茨

我认为这很容易解决。您可以确保使用regex击中所需的URL /路由后立即运行内容脚本。是否尝试过?
Prakash Palnati

0

如果您尝试检测来自任何网站的任何扩展名,则此帖子有所帮助: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)
}

0

这是另一种现代方法:

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')
})
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.