Überprüfen Sie, ob auf dem Benutzer eine Chrome-Erweiterung installiert ist

96

Ich bin gerade dabei, eine Chrome-Erweiterung zu erstellen. Damit das Ganze so funktioniert, wie ich es gerne hätte, benötige ich ein externes JavaScript-Skript, um erkennen zu können, ob ein Benutzer meine Erweiterung installiert hat.

Beispiel: Ein Benutzer installiert mein Plugin und wechselt dann zu einer Website mit meinem Skript. Die Website erkennt, dass meine Erweiterung installiert ist, und aktualisiert die Seite entsprechend.

Ist das möglich?

Yehuda Katz
quelle
2
Ja, es ist möglich, Erweiterungen zu erkennen, solange Sie Ihre Erweiterungs-ID kennen (was Sie sicher tun). Weitere Informationen finden Sie auf dieser Website: blog.kotowicz.net/2012/02/intro-to-chrome-addons-hacking.html Fahren Sie mit dem Abschnitt "Finden Sie Ihre Addons nacheinander" fort. Viel Glück!
Martin Hughes
Der richtige Weg, dies umzusetzen, wird von BJury unten beschrieben.
Rahatur
Dieser Beitrag hat geholfen: ide.hey.network/post/5c3b6c7aa7af38479accc0c7
nab.

Antworten:

46

Ich bin mir sicher, dass es einen direkten Weg gibt (Funktionen direkt auf Ihrer Erweiterung aufrufen oder die JS-Klassen für Erweiterungen verwenden), aber eine indirekte Methode (bis etwas Besseres eintritt):

Lassen Sie Ihre Chrome-Erweiterung nach einem bestimmten DIV oder einem anderen Element auf Ihrer Seite mit einer ganz bestimmten ID suchen.

Beispielsweise:

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

Tun Sie a getElementByIdund stellen Sie die innerHTMLauf die Versionsnummer Ihrer Erweiterung oder etwas ein. Sie können dann den Inhalt dieser Clientseite lesen.

Auch hier sollten Sie eine direkte Methode verwenden, falls eine verfügbar ist.


EDIT: Direkte Methode gefunden !!

Verwenden Sie die hier aufgeführten Verbindungsmethoden: https://developer.chrome.com/extensions/extension#global-events

Ungetestet, aber Sie sollten in der Lage sein ...

var myPort=chrome.extension.connect('yourextensionid_qwerqweroijwefoijwef', some_object_to_send_on_connect);
Brad
quelle
2
hmmm chrome.extension.connect scheint nur zu funktionieren, wenn es innerhalb der Erweiterung (oder einer beliebigen Erweiterung) ausgeführt wird. Ich brauche es, um mit einem zufälligen JS-Skript zu arbeiten. Irgendwelche Ideen?
Seltsamerweise sagt die Dokumentation, dass es funktionieren sollte. „ Im Gegensatz zu dem anderen Chrom. * APIs, Teile von chrome.extension können durch Content - Skripte verwendet werden“ und es Listen sendRequest(), onRequest, connect(), onRequest, und getURL().
Brad
@James führen Sie das Skript aus, das .connect () aus einem gehosteten Skript verwendet? Ich weiß, dass Chrome sich bemüht, nicht nur lokale Dateien zu verwenden, die aus Sicherheitsgründen nicht gehostet werden. - Nur nachsehen.
JamesEggers
@James das Skript, von dem ich .connect () ausführe, befindet sich auf demselben Server, wenn Sie das meinen?
23
Die letzte Methode ist nicht mehr gültig , da die connectFunktion in den chrome.runtimeNamespace verschoben wurde . Siehe BJury Antwort (und Kommentare) für eine aktuellere Version
Xan
115

Chrome kann jetzt Nachrichten von der Website an die Erweiterung senden.

Fügen Sie in der Erweiterung background.js (content.js funktioniert nicht) Folgendes hinzu:

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

Auf diese Weise können Sie von der Website aus einen Anruf tätigen:

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;
        }
    });

Sie können dann die Variable hasExtension überprüfen. Der einzige Nachteil ist, dass der Aufruf asynchron ist, also müssen Sie das irgendwie umgehen.

Bearbeiten: Wie unten erwähnt, müssen Sie der Datei manifest.json einen Eintrag hinzufügen, in dem die Domänen aufgeführt sind, die Ihrem Addon eine Nachricht senden können. Z.B:

"externally_connectable": {
    "matches": ["*://localhost/*", "*://your.domain.com/*"]
},
BJury
quelle
2
Das funktioniert wie ein Zauber. Ein weiterer Nachteil ist natürlich, dass Sie die Erweiterung steuern müssen. Sie können dies nicht verwenden, um festzustellen, ob eine beliebige Erweiterung eines Drittanbieters installiert ist.
Eric P
2
@EricP Die ursprüngliche Frage besagte, dass sie die Erweiterung geschrieben haben, daher ist das Problem umstritten.
BJury
11
Sie müssen außerdem Folgendes zu Ihrer manifest.json hinzufügen: "externally_connectable": {" match ": [" : // .yourdomain.com / *"]}
noname
3
Es sollte {version: '1.0'} und nicht {version: 1.0} sein, sonst wird in der Erweiterung Inspect view console 'Uncaught SyntaxError: Unexpected number' angezeigt.
ET-CS
1
Auf der Seite "Überprüfen" (die Webseite, die versucht, die Verfügbarkeit einer bestimmten Erweiterung zu überprüfen) ist chrome.runtime undefiniert, Chrome 36 unter Linux.
wirklich schön
22

Eine andere Methode besteht darin , eine über das Internet zugängliche Ressource verfügbar zu machen. Auf diese Weise kann jede Website testen, ob Ihre Erweiterung installiert ist.

Angenommen, die ID Ihrer Erweiterung lautet aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaund Sie fügen eine Datei (z. B. ein transparentes Pixelbild) wie test.pngin den Dateien Ihrer Erweiterung hinzu.

Anschließend machen Sie diese Datei mit dem web_accessible_resourcesManifestschlüssel für die Webseiten verfügbar :

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

Auf Ihrer Webseite können Sie versuchen, diese Datei über ihre vollständige URL zu laden (in einem <img>Tag, über XHR oder auf andere Weise):

chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/test.png

Wenn die Datei geladen wird, wird die Erweiterung installiert. Wenn beim Laden dieser Datei ein Fehler auftritt, wird die Erweiterung nicht installiert.

// 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); 
  };
}

Hinweis: Wenn beim Laden dieser Datei ein Fehler auftritt , wird der Netzwerkstapelfehler in der Konsole angezeigt, ohne dass eine Stummschaltung möglich ist. Als Chromecast diese Methode verwendete, sorgte dies aus diesem Grund für einige Kontroversen . mit der letztendlich sehr hässlichen Lösung, ganz bestimmte Fehler von Dev Tools vom Chrome-Team einfach auf die schwarze Liste zu setzen .


Wichtiger Hinweis: Diese Methode funktioniert nicht in Firefox WebExtensions. Über das Internet zugängliche Ressourcen setzen die Erweiterung von Natur aus Fingerabdrücken aus, da die URL anhand der ID vorhersehbar ist. Firefox hat beschlossen, diese Lücke zu schließen, indem es webfähigen Ressourcen eine instanzspezifische zufällige URL zuweist :

Die Dateien sind dann unter folgender URL verfügbar:

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

Diese UUID wird zufällig für jede Browserinstanz generiert und ist nicht die ID Ihrer Erweiterung. Dies verhindert, dass Websites die von einem Benutzer installierten Erweiterungen mit einem Fingerabdruck versehen.

Obwohl die Erweiterung runtime.getURL()zum Abrufen dieser Adresse verwendet werden kann, können Sie sie auf Ihrer Website nicht fest codieren.

Xan
quelle
Obwohl diese Antwort den "Saft" von stackoverflow.com/a/9216924/1504300 erhält , fügt IMHO einige ziemlich wichtige Informationen hinzu, wie das Offenlegen der Ressource in der Erweiterung und die Tatsache, dass Sie eine Ajax-Anforderung zum Überprüfen der Existenz verwenden können (Bildobjekt) scheint nur auf HTML5 verfügbar zu sein, wenn ich mich nicht irre goo.gl/HBeI1i ). Mit den Informationen in dieser Antwort konnte ich das Problem lösen und fand es wie eine "out of the box" -Lösung
wirklich schön
@niconic Diese Antwort (die ohnehin nur als Link schlecht ist) bezieht sich auf die Situation vor dem Inkrafttreten von Manifest Version 2. Bisher musste man keine Ressourcen für den Webzugriff deklarieren.
Xan
19

Ich dachte, ich würde meine Forschung darüber teilen. Ich musste in der Lage sein zu erkennen, ob eine bestimmte Erweiterung installiert wurde, damit einige file: /// Links funktionieren. Ich bin hier auf diesen Artikel gestoßen. Dies erklärte eine Methode zum Abrufen der manifest.json einer Erweiterung.

Ich habe den Code ein wenig angepasst und mir Folgendes ausgedacht:

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'); };
}

Damit sollten Sie Ext_Detect (ExtensionName, ExtensionID) verwenden können, um die Installation einer beliebigen Anzahl von Erweiterungen zu erkennen.

Nato
quelle
2
Es sieht so aus, als hätte Google die Sicherheit erhöht. Beim Ausführen von Ext_Detect () wird der folgende Fehler angezeigt: Verweigern des
Lounge9
Ich kann dies mit Version 32.0.1700.107 m von Chrome vom 27.02.2014
JE Carter II
1
wie @ Lounge9 sagte. Ressourcen in Paketen, die manifest_version 2 (oder höher) verwenden, sind standardmäßig blockiert und müssen für die Verwendung über diese Eigenschaft in die Whitelist aufgenommen werden, indem zu manifest.json Folgendes hinzugefügt wird: "web_accessible_resources": ["manifest..json"],
ET-CS
Mit @BJury answer können Sie auch problemlos Daten von der Erweiterung an das Skript übergeben (z. B. die Erweiterungsversion), und Sie müssen keine Datei aus der Erweiterung verfügbar machen.
ET-CS
1
Dies hat bei mir am besten funktioniert, da unsere Erweiterung für mehrere Domänen verwendet wird und nicht vordefiniert werden konnte, da regelmäßig neue Domänen hinzugefügt werden. Anstatt auf manifest.json zuzugreifen, habe ich eine neue Datei version.json erstellt und die Versionsnummer eingegeben. Das funktioniert genauso.
Paul Haggo
7

Eine andere mögliche Lösung, wenn Sie die Website besitzen, ist die Inline-Installation .

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

Ich weiß, dass dies eine alte Frage ist, aber diese Methode wurde in Chrome 15 eingeführt. Daher dachte ich, ich würde sie für alle auflisten, die erst jetzt nach einer Antwort suchen.

PAEz
quelle
12
Dies funktioniert gut für eine Chrome- App , aber nicht für eine Chrome-Erweiterung AFAIK
Eran Medan
Ja, auf dieser Website erfahren Sie, wie Sie eine Erweiterung inline installieren , empfehlen jedoch anscheinend "Erweiterungen können über Inhaltsskripte mit der Einbettungsseite kommunizieren, um sie darüber zu informieren, dass sie bereits installiert sind." anstatt chrome.app.isInstalled verwenden zu können. Verwirrte mich auch ...
Rogerdpack
4

Ich habe die Cookie-Methode verwendet:

In meine Datei manifest.js habe ich ein Inhaltsskript aufgenommen, das nur auf meiner Website ausgeführt wird:

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

In meiner js / mysite.js habe ich eine Zeile:

document.cookie = "extension_downloaded=True";

und auf meiner index.html-Seite suche ich nach diesem Cookie.

if (document.cookie.indexOf('extension_downloaded') != -1){
    document.getElementById('install-btn').style.display = 'none';
}
Chase Roberts
quelle
Ich habe alle oben genannten Lösungen ausprobiert, funktioniert aber nicht. Dann sehe ich Ihre Antwort. Das ist es, wonach ich suche!
John Doe
Dies erhöht fortan den Aufwand für jede HTTP-Anforderung.
mlissner
3

Sie können die Erweiterung ein Cookie setzen lassen und JavaScript auf Ihren Websites überprüfen lassen, ob dieses Cookie vorhanden ist, und es entsprechend aktualisieren. Diese und wahrscheinlich die meisten anderen hier erwähnten Methoden können natürlich vom Benutzer umgangen werden, es sei denn, Sie versuchen, die Erweiterung benutzerdefinierte Cookies in Abhängigkeit von Zeitstempeln usw. erstellen zu lassen und Ihre Anwendung sie serverseitig analysieren zu lassen, um festzustellen, ob es sich wirklich um einen Benutzer mit dem handelt Erweiterung oder jemand, der vorgibt, es zu haben, indem er seine Cookies ändert.

Niklas
quelle
5
Das einzige Problem ist, dass ein Benutzer Ihre Erweiterungen löscht. Der Cookie bleibt wahrscheinlich gesetzt.
Chase Roberts
3

In diesem Beitrag von Google Groups wird eine andere Methode gezeigt . Kurz gesagt, Sie können versuchen festzustellen, ob das Erweiterungssymbol erfolgreich geladen wurde. Dies kann hilfreich sein, wenn die Erweiterung, nach der Sie suchen, nicht Ihre eigene ist.

Beau
quelle
1
OK. Wie prüfen wir, ob ein Erweiterungssymbol vorhanden ist?
Michael Rogers
3

Die Webseite interagiert mit der Erweiterung über ein Hintergrundskript.

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>
Dawid Szymański
quelle
Funktioniert nicht mit Firefox WebExtensions, bei denen extern_connectable nicht unterstützt wird.
mlissner
3

Ihre Erweiterung könnte mit der Website interagieren (z. B. Variablen ändern) und Ihre Website könnte dies erkennen.

Aber es sollte einen besseren Weg geben, dies zu tun. Ich frage mich, wie Google das in seiner Erweiterungsgalerie macht (bereits installierte Anwendungen sind markiert).

Bearbeiten:

Die Galerie verwendet die Funktion chrome.management.get . Beispiel:

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

Sie können jedoch nur von Seiten mit den richtigen Berechtigungen auf die Methode zugreifen.

Fox32
quelle
1
ye, die die Erweiterung erfordern würde, um mit jeder Site auf jeder Registerkarte zu interagieren, die langsam / schwer zu implementieren und
Das Problem ist, dass eine Kommunikation in die andere Richtung (Seite zur Erweiterung) aufgrund des Sicherheitsmodells von Chrome nicht möglich ist. Wenn Sie nicht auf "interaktive" Weise vorgehen möchten, verwenden Sie die Cookie-Methode.
Fox32
2
Sehr geehrter @ Fox32, chrome.management.get ..., gibt diesen Fehler zurück:Uncaught TypeError: Cannot read property 'get' of undefined
Hosein Aqajani
3

Viele der Antworten hier sind bisher nur Chrome oder verursachen eine HTTP-Overhead-Strafe. Die Lösung, die wir verwenden, ist etwas anders:

1. Fügen Sie der Liste der manifestierten content_scripts ein neues Objekt wie folgt hinzu:

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

Dadurch kann der Code in install_notifier.js auf dieser Site ausgeführt werden (sofern Sie dort noch keine Berechtigungen hatten).

2. Senden Sie eine Nachricht an jede Site im obigen Manifestschlüssel.

Fügen Sie install_notifier.js so etwas hinzu (beachten Sie, dass dies einen Abschluss verwendet, um zu verhindern, dass die Variablen global sind, dies ist jedoch nicht unbedingt erforderlich):

// 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
  }, "*");
})();

Ihre Nachricht könnte alles sagen, aber es ist nützlich, die Version zu senden, damit Sie wissen, womit Sie es zu tun haben. Dann...

3. Hören Sie auf Ihrer Website auf diese Nachricht.

Fügen Sie dies irgendwo zu Ihrer Website hinzu:

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");
  }
});

Dies funktioniert in Firefox und Chrome und verursacht keinen HTTP-Overhead oder manipuliert die Seite.

mlissner
quelle
0

Wenn Sie die Kontrolle über die Chrome-Erweiterung haben, können Sie versuchen, was ich getan habe:

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

Und dann:

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

}

Es fühlt sich ein wenig hackig an, aber ich konnte die anderen Methoden nicht zum Laufen bringen, und ich mache mir Sorgen, dass Chrome hier seine API ändert. Es ist zweifelhaft, dass diese Methode bald nicht mehr funktioniert.

gwg
quelle
Wie oben erwähnt - ich hatte dies getestet, aber die Reihenfolge der Operationen scheint seltsam - welches Skript wird zuerst ausgeführt usw.?
Brady Moritz
0

Sie können auch eine browserübergreifende Methode verwenden, die ich verwendet habe. Verwendet das Konzept des Hinzufügens eines Div.

in Ihrem Inhaltsskript (wann immer das Skript geladen wird, sollte es dies tun)

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

Auf Ihrer Website behaupten Sie so etwas wie:

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

Und entsprechende Nachricht werfen.

Prakash Palnati
quelle
Ich hatte dies getestet, aber die Reihenfolge der Operationen scheint seltsam - welches Skript wird zuerst ausgeführt usw.?
Brady Moritz
@BradyMoritz die gleiche Reihenfolge wie in der Antwort. Fügen Sie zuerst die Klasse hinzu und bestätigen Sie dann.
Prakash Palnati
Es schien, als würde mein Inhaltsskript nicht unbedingt vor meinem In-Page-Skript ausgeführt.
Brady Moritz
Das finde ich einfach zu beheben. Sie können sicherstellen, dass Ihr Inhaltsskript direkt nach dem Aufrufen der erforderlichen URL / Route mit regex ausgeführt wird. Haben Sie das versucht?
Prakash Palnati
0

Wenn Sie versuchen, eine Erweiterung von einer Website zu erkennen, hat dieser Beitrag geholfen: https://ide.hey.network/post/5c3b6c7aa7af38479accc0c7

Grundsätzlich besteht die Lösung darin, einfach zu versuchen, eine bestimmte Datei (manifest.json oder ein Bild) aus der Erweiterung abzurufen, indem der Pfad angegeben wird. Folgendes habe ich verwendet. Auf jeden Fall funktioniert:

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)
}
schnappen.
quelle
0

Hier ist ein anderer moderner Ansatz:

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')
})
K-Gun
quelle