我正在构建Chrome扩展程序,为了使整个工作正常进行,我需要一个外部JavaScript脚本来检测用户是否安装了我的扩展程序。
例如:用户安装了我的插件,然后访问了带有我的脚本的网站。该网站检测到我的扩展程序已安装,并相应地更新页面。
这可能吗?
我正在构建Chrome扩展程序,为了使整个工作正常进行,我需要一个外部JavaScript脚本来检测用户是否安装了我的扩展程序。
例如:用户安装了我的插件,然后访问了带有我的脚本的网站。该网站检测到我的扩展程序已安装,并相应地更新页面。
这可能吗?
Answers:
我敢肯定,有一种直接方法(直接在扩展程序上调用函数,或通过使用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);
sendRequest()
,onRequest
,connect()
,onRequest
,和getURL()
。
connect
函数已移至chrome.runtime
名称空间。有关最新版本,请参阅BJury的答案(和评论)
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/*"]
},
另一种方法是公开可访问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()
用来获取此地址,但是您不能在网站中对其进行硬编码。
我以为我会分享我对此的研究。我需要能够检测是否为某些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)来检测任意数量的扩展的安装。
如果您拥有网站,另一种可能的解决方案是使用嵌入式安装。
if (chrome.app.isInstalled) {
// extension is installed.
}
我知道这是一个古老的问题,但是Chrome 15引入了这种方式,因此我认为Id仅将其列出给现在正在寻找答案的人。
我使用了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';
}
您可以让扩展程序设置一个cookie,并让您的网站JavaScript检查该cookie是否存在并相应地进行更新。除非您尝试让扩展程序根据时间戳等创建自定义Cookie,并让您的应用程序在服务器端对其进行分析,以查看它是否真的是用户,否则,用户可能当然会忽略这里提到的这种方法以及大多数其他方法。扩展名或通过修改Cookie假装拥有该扩展名的人。
此Google网上论坛帖子中显示了另一种方法。简而言之,您可以尝试检测扩展程序图标是否成功加载。如果您要检查的扩展名不是您自己的扩展名,这可能会有所帮助。
网页通过后台脚本与扩展名进行交互。
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>
您的扩展程序可以与网站交互(例如,更改变量),并且您的网站可以检测到这一点。
但是应该有一个更好的方法来做到这一点。我不知道Google如何在其扩展程序库(已标记已安装的应用程序)上做到这一点。
编辑:
画廊使用chrome.management.get函数。例:
chrome.management.get("mblbciejcodpealifnhfjbdlkedplodp", function(a){console.log(a);});
但是您只能从具有适当权限的页面访问该方法。
Uncaught TypeError: Cannot read property 'get' of undefined
到目前为止,这里的许多答案仅是Chrome或会产生HTTP开销损失。我们使用的解决方案略有不同:
{
"matches": ["https://www.yoursite.com/*"],
"js": [
"install_notifier.js"
],
"run_at": "document_idle"
}
这将允许install_notifier.js中的代码在该站点上运行(如果您在那里没有权限)。
在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
}, "*");
})();
您的消息可以说什么,但发送版本很有用,这样您就可以知道要处理的内容。然后...
将此添加到您的网站:
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开销或操纵页面。
如果您可以控制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。令人怀疑的是,这种方法将很快停止工作。
您还可以使用我使用的跨浏览器方法。使用添加div的概念。
在您的内容脚本中(无论脚本何时加载,都应该这样做)
if ((window.location.href).includes('*myurl/urlregex*')) {
$('html').addClass('ifextension');
}
在您的网站中,您断言类似,
if (!($('html').hasClass('ifextension')){}
并抛出适当的消息。
如果您尝试检测来自任何网站的任何扩展名,则此帖子有所帮助: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)
}
这是另一种现代方法:
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')
})