Controlla se l'utente ha un'estensione di Chrome installata


97

Sono in procinto di creare un'estensione di Chrome e, affinché tutto funzioni come vorrei, ho bisogno di uno script JavaScript esterno per poter rilevare se un utente ha installato la mia estensione.

Ad esempio: un utente installa il mio plug-in, quindi visita un sito Web con il mio script. Il sito Web rileva che la mia estensione è installata e aggiorna la pagina di conseguenza.

È possibile?


2
Sì, è possibile rilevare le estensioni, a condizione che tu conosca l'ID dell'estensione (cosa che sono sicuro che conosci). Controlla questo sito per maggiori informazioni: blog.kotowicz.net/2012/02/intro-to-chrome-addons-hacking.html Passa alla sezione "Trovare i tuoi componenti aggiuntivi uno per uno". In bocca al lupo!
Martin Hughes,

Il modo corretto per implementarlo è descritto da BJury sotto.
Rahatur

Risposte:


46

Sono sicuro che esista un modo diretto (chiamando le funzioni sulla tua estensione direttamente o utilizzando le classi JS per le estensioni), ma un metodo indiretto (fino a quando non arriva qualcosa di meglio):

Chiedi all'estensione di Chrome di cercare un DIV specifico o un altro elemento sulla tua pagina, con un ID molto specifico.

Per esempio:

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

Fai un getElementByIde imposta il innerHTMLnumero di versione della tua estensione o qualcosa del genere. È quindi possibile leggere il contenuto di quel lato client.

Di nuovo, però, dovresti usare un metodo diretto se ce n'è uno disponibile.


EDIT: metodo diretto trovato !!

Utilizza i metodi di connessione disponibili qui: https://developer.chrome.com/extensions/extension#global-events

Non testato, ma dovresti essere in grado di fare ...

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

2
hmmm chrome.extension.connect sembra funzionare solo se eseguito dall'interno dell'estensione (o da qualsiasi estensione). Ho bisogno che funzioni da qualsiasi script js casuale. Qualche idea?

Strano, la documentazione dice che dovrebbe funzionare. "A differenza degli altri cromato. * API, parti di chrome.extension possono essere utilizzate dagli script di contenuti" e le liste sendRequest(), onRequest, connect(), onRequest, e getURL().
Brad

@ James stai eseguendo lo script che utilizza .connect () da uno script ospitato? So che Chrome si sforza di non fare cose con solo file locali che non sono ospitati per motivi di sicurezza. - Solo controllando.
JamesEggers

@ James, lo script da cui sto eseguendo .connect () è sullo stesso server, se è questo che intendi?

23
L'ultimo metodo non è più valido , poiché la connectfunzione è stata spostata nello chrome.runtimespazio dei nomi. Vedi la risposta (e i commenti) di BJury per una versione più aggiornata
Xan

117

Chrome ora ha la possibilità di inviare messaggi dal sito Web all'estensione.

Quindi nell'estensione background.js (content.js non funzionerà) aggiungi qualcosa come:

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

Questo ti consentirà quindi di effettuare una chiamata dal sito Web:

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

È quindi possibile controllare la variabile hasExtension. L'unico inconveniente è che la chiamata è asincrona, quindi devi aggirarlo in qualche modo.

Modifica: come accennato di seguito, dovrai aggiungere una voce al manifest.json che elenca i domini che possono inviare messaggi al tuo componente aggiuntivo. Per esempio:

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

2
Funziona come un fascino. Un altro svantaggio è ovviamente che devi controllare l'estensione: non puoi usarlo per vedere se è installata un'estensione arbitraria di terze parti.
Eric P

2
@EricP La domanda originale affermava che stavano scrivendo l'estensione, quindi il problema è discutibile.
giuria

13
Dovrai anche aggiungere quanto segue al tuo manifest.json: "externally_connectable": {"match": [" : // .yourdomain.com / *"]}
noname

3
Dovrebbe essere {version: '1.0'} e non {version: 1.0} altrimenti riceverai "Uncaught SyntaxError: Unexpected number" nell'estensione Inspect view console.
ET-CS

1
Sul lato del "controllo" (la pagina web che cerca di verificare la disponibilità di una data estensione), chrome.runtime è indefinito, chrome 36 su linux.
davvero bello l'

22

Un altro metodo consiste nell'esposizione di una risorsa accessibile dal Web , sebbene ciò consentirà a qualsiasi sito Web di verificare se l'estensione è installata.

Supponi che l'ID della tua estensione sia aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaae aggiungi un file (ad esempio, un'immagine pixel trasparente) come test.pngnei file della tua estensione.

Quindi, esponi questo file alle pagine web con la web_accessible_resourceschiave manifest:

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

Nella tua pagina web, puoi provare a caricare questo file dal suo URL completo (in un <img>tag, tramite XHR o in qualsiasi altro modo):

chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/test.png

Se il file viene caricato, l'estensione viene installata. Se si verifica un errore durante il caricamento di questo file, l'estensione non è installata.

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

Nota: se si verifica un errore durante il caricamento di questo file, tale errore di stack di rete apparirà nella console senza possibilità di silenziarlo. Quando Chromecast ha utilizzato questo metodo, ha causato un bel po 'di polemiche per questo motivo; con l'eventuale soluzione molto brutta di semplicemente inserire nella lista nera errori molto specifici da Dev Tools del tutto da parte del team di Chrome.


Nota importante: questo metodo non funzionerà in Firefox WebExtensions. Le risorse accessibili dal Web espongono intrinsecamente l'estensione all'impronta digitale, poiché l'URL è prevedibile conoscendo l'ID. Firefox ha deciso di chiudere quel buco assegnando un URL casuale specifico dell'istanza alle risorse accessibili dal Web:

I file saranno quindi disponibili utilizzando un URL come:

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

Questo UUID viene generato in modo casuale per ogni istanza del browser e non è l'ID dell'estensione. Ciò impedisce ai siti Web di rilevare le impronte delle estensioni installate da un utente.

Tuttavia, sebbene l'estensione possa essere utilizzata runtime.getURL()per ottenere questo indirizzo, non è possibile codificarlo come hardcoded nel tuo sito web.


Sebbene questa risposta ottenga il "succo" da stackoverflow.com/a/9216924/1504300 , IMHO aggiunge alcune informazioni piuttosto importanti come l'esposizione della risorsa nell'estensione e il fatto che puoi utilizzare una richiesta ajax per verificare l'esistenza (oggetto Immagine sembra disponibile solo su HTML5 se non sbaglio goo.gl/HBeI1i ). Con le informazioni in questa risposta sono stato in grado di risolvere il problema, l'ho trovata come una soluzione "fuori dagli schemi"
davvero bella

@niconic Quella risposta (comunque non valida come link-only) si riferisce alla situazione prima dell'entrata in vigore della versione 2 di manifest. In precedenza, non era necessario dichiarare le risorse accessibili dal Web.
Xan

19

Ho pensato di condividere la mia ricerca su questo. Avevo bisogno di essere in grado di rilevare se un'estensione specifica fosse installata perché alcuni collegamenti file: /// funzionassero. Mi sono imbattuto in questo articolo qui Questo ha spiegato un metodo per ottenere il manifest.json di un'estensione.

Ho modificato un po 'il codice e ho pensato:

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

Con questo dovresti essere in grado di utilizzare Ext_Detect (ExtensionName, ExtensionID) per rilevare l'installazione di qualsiasi numero di estensioni.


2
Sembra che Google abbia reso le cose più sicure, ottengo il seguente errore quando eseguo Ext_Detect (): Negazione del caricamento di chrome-extension: // [my_extension_id] /manifest.json. Le risorse devono essere elencate nella chiave manifest web_accessible_resources per poter essere caricate da pagine esterne all'estensione.
Lounge9

Sono in grado di farlo funzionare con la versione 32.0.1700.107 m di Chrome a partire dal 27/02/2014
JE Carter II

1
come ha detto @ Lounge9. Le risorse all'interno dei pacchetti che utilizzano manifest_version 2 (o superiore) sono bloccate per impostazione predefinita e devono essere autorizzate per essere utilizzate tramite questa proprietà aggiungendo a manifest.json: "web_accessible_resources": ["manifest..json"],
ET-CS

Usando la risposta @BJury puoi anche passare facilmente i dati dall'estensione allo script (ad esempio la versione dell'estensione) e non è necessario esporre alcun file dall'estensione.
ET-CS

1
Questo ha funzionato meglio per me perché la nostra estensione verrà utilizzata su più domini e non può essere predefinita poiché vengono aggiunti regolarmente nuovi domini. Invece di accedere a manifest.json, ho creato un nuovo file version.json e ho inserito il numero di versione all'interno. Funziona allo stesso modo.
Paul Haggo

7

Un'altra possibile soluzione se si è proprietari del sito Web è utilizzare l'installazione in linea .

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

So che questa è una vecchia domanda, ma in questo modo è stata introdotta in Chrome 15 e quindi ho pensato di elencarla per chiunque solo ora cerchi una risposta.


12
Funziona alla grande per un'app Chrome , ma non per un'estensione Chrome AFAIK
Eran Medan

Sì, quel sito web ti dice come installare in linea un'estensione, ma a quanto pare consiglia "Le estensioni possono comunicare con la pagina di incorporamento tramite script di contenuto per farle sapere che sono già installate." invece di poter utilizzare chrome.app.isInstalled. Confuso anche me ...
rogerdpack


4

Ho utilizzato il metodo dei cookie:

Nel mio file manifest.js ho incluso uno script di contenuto che viene eseguito solo sul mio sito:

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

nel mio js / mysite.js ho una riga:

document.cookie = "extension_downloaded=True";

e nella mia pagina index.html cerco quel cookie.

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

Ho provato tutta la soluzione sopra ma non funziona, quindi vedo la tua risposta, questo è quello che sto cercando!
John Doe,

D'ora in poi questo aggiunge overhead a ogni richiesta HTTP.
mlissner

3

È possibile che l'estensione imposti un cookie e che JavaScript controlli se il cookie è presente e si aggiorni di conseguenza. Questo e probabilmente la maggior parte degli altri metodi qui menzionati potrebbero ovviamente essere evitati dall'utente, a meno che tu non provi a fare in modo che l'estensione crei cookie personalizzati a seconda dei timestamp, ecc., E la tua applicazione li analizzi lato server per vedere se è davvero un utente con il estensione o qualcuno che finge di averlo modificando i suoi cookie.


5
l'unico problema è che se un utente elimina le tue estensioni. Il cookie rimarrà probabilmente impostato.
Chase Roberts

3

C'è un altro metodo mostrato in questo post di Google Gruppi . In breve, potresti provare a rilevare se l'icona dell'estensione viene caricata correttamente. Questo può essere utile se l'estensione che stai cercando non è la tua.


1
Ok. Come controlliamo se esiste un'icona di estensione?
Michael Rogers

3

La pagina web interagisce con l'estensione tramite script in background.

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>

Non funziona su Firefox WebExtensions, dove externally_connectable non è supportato.
mlissner

3

La tua estensione potrebbe interagire con il sito web (ad esempio modificando le variabili) e il tuo sito web potrebbe rilevarlo.

Ma dovrebbe esserci un modo migliore per farlo. Mi chiedo come fa Google a farlo sulla loro galleria di estensioni (le applicazioni già installate sono contrassegnate).

Modificare:

La galleria utilizza la funzione chrome.management.get . Esempio:

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

Ma puoi accedere al metodo solo dalle pagine con le giuste autorizzazioni.


1
tu ciò richiederebbe l'estensione per interagire con ogni sito su ogni scheda che sarebbe lento / difficile da implementare e con bug: - /

Il problema è che la comunicazione nell'altra direzione (dalla pagina all'estensione) non è possibile, a causa del modello di sicurezza di Chrome. Se non vuoi andare in modalità "interagente", scegli la modalità dei cookie.
Fox32

2
Gentile @ Fox32, chrome.management.get ..., restituisce questo errore:Uncaught TypeError: Cannot read property 'get' of undefined
Hosein Aqajani

3

Molte delle risposte qui finora sono solo Chrome o comportano una penalità di overhead HTTP. La soluzione che stiamo usando è leggermente diversa:

1. Aggiungi un nuovo oggetto all'elenco manifest content_scripts in questo modo:

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

Ciò consentirà al codice in install_notifier.js di funzionare su quel sito (se non si dispone già delle autorizzazioni lì).

2. Invia un messaggio a ogni sito nella chiave manifest sopra.

Aggiungi qualcosa di simile a install_notifier.js (nota che questo utilizza una chiusura per evitare che le variabili siano globali, ma non è strettamente necessario):

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

Il tuo messaggio potrebbe dire qualsiasi cosa, ma è utile inviare la versione in modo da sapere con cosa hai a che fare. Poi...

3. Sul tuo sito web, ascolta quel messaggio.

Aggiungi questo al tuo sito web da qualche parte:

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

Funziona in Firefox e Chrome e non incorre in overhead HTTP né manipola la pagina.


0

Se hai il controllo sull'estensione di Chrome, puoi provare quello che ho fatto:

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

E poi:

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

}

Sembra un po 'complicato, ma non sono riuscito a far funzionare gli altri metodi e mi preoccupo che Chrome cambi la sua API qui. È dubbio che questo metodo smetterà di funzionare presto.


come accennato in precedenza, l'ho testato ma l'ordine delle operazioni sembra strano, quale script viene eseguito per primo, ecc.?
Brady Moritz

0

Puoi anche usare un metodo cross-browser quello che ho usato. Utilizza il concetto di aggiunta di un div.

nello script del contenuto (ogni volta che lo script viene caricato, dovrebbe farlo)

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

nel tuo sito web asserisci qualcosa come

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

E lancia un messaggio appropriato.


L'avevo testato ma l'ordine delle operazioni sembra strano: quale script viene eseguito per primo, ecc.?
Brady Moritz

@ BradyMoritz nello stesso ordine della risposta. Aggiungi prima la classe e poi asserisci.
Prakash Palnati

sembrava che il mio script di contenuto non fosse necessariamente in esecuzione prima del mio script in-page?
Brady Moritz

Questo, penso sia facile da risolvere. Puoi assicurarti che lo script del contenuto venga eseguito subito dopo aver raggiunto l'URL / percorso richiesto utilizzando regex. L'hai provato?
Prakash Palnati

0

Se stai cercando di rilevare qualsiasi estensione da qualsiasi sito web, questo post ha aiutato: https://ide.hey.network/post/5c3b6c7aa7af38479accc0c7

Fondamentalmente, la soluzione sarebbe semplicemente provare a ottenere un file specifico (manifest.json o un'immagine) dall'estensione specificandone il percorso. Ecco cosa ho usato. Sicuramente funzionante:

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

Ecco un altro approccio moderno:

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')
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.