可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am in the process of building a Chrome extension, and for the whole thing to work the way I would like it to, I need an external JavaScript script to be able to detect if a user has my extension installed.
For example: A user installs my plugin, then goes to a website with my script on it. The website detects that my extension is installed and updates the page accordingly.
Is this possible?
回答1:
I am sure there is a direct way (calling functions on your extension directly, or by using the JS classes for extensions), but an indirect method (until something better comes along):
Have your Chrome extension look for a specific DIV or other element on your page, with a very specific ID.
For example:
<div id=\"ExtensionCheck_JamesEggersAwesomeExtension\"></div>
Do a getElementById
and set the innerHTML
to the version number of your extension or something. You can then read the contents of that client-side.
Again though, you should use a direct method if there is one available.
EDIT: Direct method found!!
Use the connection methods found here: https://developer.chrome.com/extensions/extension#global-events
Untested, but you should be able to do...
var myPort=chrome.extension.connect(\'yourextensionid_qwerqweroijwefoijwef\', some_object_to_send_on_connect);
回答2:
Chrome now has the ability to send messages from the website to the extension.
So in the extension background.js (content.js will not work) add something like:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request) {
if (request.message) {
if (request.message == \"version\") {
sendResponse({version: 1.0});
}
}
}
return true;
});
This will then let you make a call from the website:
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;
}
});
You can then check the hasExtension variable. The only drawback is the call is asynchronous, so you have to work around that somehow.
Edit:
As mentioned below, you\'ll need to add an entry to the manifest.json listing the domains that can message your addon. Eg:
\"externally_connectable\": {
\"matches\": [\"*://localhost/*\", \"*://your.domain.com/*\"]
},
回答3:
Another method is to expose a web-accessible resource, though this will allow any website to test if your extension is installed.
Suppose your extension\'s ID is aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
, and you add a file (say, a transparent pixel image) as test.png
in your extension\'s files.
Then, you expose this file to the web pages with web_accessible_resources
manifest key:
\"web_accessible_resources\": [
\"test.png\"
],
In your web page, you can try to load this file by its full URL (in an <img>
tag, via XHR, or in any other way):
chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/test.png
If the file loads, then the extension is installed. If there\'s an error while loading this file, then the extension is not installed.
// 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);
};
}
Of note: if there is an error while loading this file, said network stack error will appear in the console with no possibility to silence it. When Chromecast used this method, it caused quite a bit of controversy because of this; with the eventual very ugly solution of simply blacklisting very specific errors from Dev Tools altogether by the Chrome team.
Important note: this method will not work in Firefox WebExtensions. Web-accessible resources inherently expose the extension to fingerprinting, since the URL is predictable by knowing the ID. Firefox decided to close that hole by assigning an instance-specific random URL to web accessible resources:
The files will then be available using a URL like:
moz-extension://<random-UUID>/<path/to/resource>
This UUID is randomly generated for every browser instance and is not your extension\'s ID. This prevents websites from fingerprinting the extensions a user has installed.
However, while the extension can use runtime.getURL()
to obtain this address, you can\'t hard-code it in your website.
回答4:
I thought I would share my research on this.
I needed to be able to detect if a specific extension was installed for some file:/// links to work.
I came across this article here
This explained a method of getting the manifest.json of an extension.
I adjusted the code a bit and came up with:
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\');};
}
With this you should be able to use Ext_Detect(ExtensionName,ExtensionID) to detect the installation of any number of extensions.
回答5:
Another possible solution if you own the website is to use inline installation.
if (chrome.app.isInstalled) {
// extension is installed.
}
I know this an old question but this way was introduced in Chrome 15 and so I thought Id list it for anyone only now looking for an answer.
回答6:
You could have the extension set a cookie and have your websites JavaScript check if that cookie is present and update accordingly. This and probably most other methods mentioned here could of course be cirvumvented by the user, unless you try and have the extension create custom cookies depending on timestamps etc, and have your application analyze them server side to see if it really is a user with the extension or someone pretending to have it by modifying his cookies.
回答7:
There\'s another method shown at this Google Groups post. In short, you could try detecting whether the extension icon loads successfully. This may be helpful if the extension you\'re checking for isn\'t your own.
回答8:
I used the cookie method:
In my manifest.js file I included a content script that only runs on my site:
\"content_scripts\": [
{
\"matches\": [
\"*://*.mysite.co/*\"
],
\"js\": [\"js/mysite.js\"],
\"run_at\": \"document_idle\"
}
],
in my js/mysite.js I have one line:
document.cookie = \"extension_downloaded=True\";
and in my index.html page I look for that cookie.
if (document.cookie.indexOf(\'extension_downloaded\') != -1){
document.getElementById(\'install-btn\').style.display = \'none\';
}
回答9:
Webpage interacts with extension through background script.
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>
回答10:
Your extension could interact with the website (e.g. changing variables) and your website could detect this.
But there should be a better way to do this. I wonder how Google is doing it on their extension gallery (already installed applications are marked).
Edit:
The gallery use the chrome.management.get function. Example:
chrome.management.get(\"mblbciejcodpealifnhfjbdlkedplodp\", function(a){console.log(a);});
But you can only access the method from pages with the right permissions.
回答11:
A lot of the answers here so far are Chrome only or incur an HTTP overhead penalty. The solution that we are using is a little different:
1. Add a new object to the manifest content_scripts list like so:
{
\"matches\": [\"https://www.yoursite.com/*\"],
\"js\": [
\"install_notifier.js\"
],
\"run_at\": \"document_idle\"
}
This will allow the code in install_notifier.js to run on that site (if you didn\'t already have permissions there).
2. Send a message to every site in the manifest key above.
Add something like this to install_notifier.js (note that this is using a closure to keep the variables from being global, but that\'s not strictly necessary):
// 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
}, \"*\");
})();
Your message could say anything, but it\'s useful to send the version so you know what you\'re dealing with. Then...
3. On your website, listen for that message.
Add this to your website somewhere:
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\");
}
});
This works in Firefox and Chrome, and doesn\'t incur HTTP overhead or manipulate the page.
回答12:
If you have control over the Chrome extension, you can try what I did:
// Inside Chrome extension
var div = document.createElement(\'div\');
div.setAttribute(\'id\', \'myapp-extension-installed-div\');
document.getElementsByTagName(\'body\')[0].appendChild(div);
And then:
// On web page that needs to detect extension
if ($(\'#myapp-extension-installed-div\').length) {
}
It feels a little hacky, but I couldn\'t get the other methods to work, and I worry about Chrome changing its API here. It\'s doubtful this method will stop working any time soon.
回答13:
You could also use a cross-browser method what I have used.
Uses the concept of adding a div.
in your content script (whenever the script loads, it should do this)
if ((window.location.href).includes(\'*myurl/urlregex*\')) {
$(\'html\').addClass(\'ifextension\');
}
in your website you assert something like,
if (!($(\'html\').hasClass(\'ifextension\')){}
And throw appropriate message.