Periksa apakah pengguna telah memasang ekstensi Chrome

98

Saya sedang dalam proses membangun ekstensi Chrome, dan agar semuanya berfungsi seperti yang saya inginkan, saya memerlukan skrip JavaScript eksternal untuk dapat mendeteksi jika pengguna telah memasang ekstensi saya.

Misalnya: Seorang pengguna menginstal plugin saya, lalu membuka situs web dengan skrip saya di dalamnya. Situs web mendeteksi bahwa ekstensi saya dipasang dan memperbarui halaman yang sesuai.

Apakah ini mungkin?

Yehuda Katz
sumber
2
Ya, ekstensi dapat dideteksi, selama Anda mengetahui ID ekstensi Anda (yang saya yakin Anda lakukan). Periksa situs ini untuk informasi lebih lanjut: blog.kotowicz.net/2012/02/intro-to-chrome-addons-hacking.html Lewati ke bawah ke bagian 'Menemukan addons Anda satu per satu'. Semoga berhasil!
Martin Hughes
Cara yang tepat untuk mengimplementasikan ini dijelaskan oleh BJury di bawah ini.
Rahatur

Jawaban:

46

Saya yakin ada cara langsung (memanggil fungsi pada ekstensi Anda secara langsung, atau dengan menggunakan kelas JS untuk ekstensi), tetapi metode tidak langsung (sampai sesuatu yang lebih baik datang):

Minta ekstensi Chrome Anda mencari DIV tertentu atau elemen lain di laman Anda, dengan ID yang sangat spesifik.

Sebagai contoh:

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

Lakukan getElementByIddan setel innerHTMLke nomor versi ekstensi Anda atau sesuatu. Anda kemudian dapat membaca konten dari sisi klien tersebut.

Namun sekali lagi, Anda harus menggunakan metode langsung jika ada.


EDIT: Metode langsung ditemukan !!

Gunakan metode koneksi yang ditemukan di sini: https://developer.chrome.com/extensions/extension#global-events

Belum teruji, tetapi Anda harus dapat melakukan ...

var myPort=chrome.extension.connect('yourextensionid_qwerqweroijwefoijwef', some_object_to_send_on_connect);
Brad
sumber
2
hmmm chrome.extension.connect sepertinya hanya berfungsi jika dijalankan dari dalam ekstensi (atau ekstensi apa pun). Saya membutuhkannya untuk bekerja dari skrip js acak apa pun. Ada ide?
Aneh, dokumentasinya mengatakan itu harus bekerja. "Berbeda dengan krom lainnya. * API, bagian dari chrome.extension dapat digunakan oleh skrip konten" dan daftar sendRequest(), onRequest, connect(), onRequest, dan getURL().
Brad
@James apakah Anda menjalankan skrip yang menggunakan .connect () dari skrip yang dihosting? Saya tahu Chrome berusaha keras untuk tidak melakukan hal-hal hanya dengan file lokal yang tidak dihosting untuk tujuan keamanan. - Hanya mengecek.
JamesEggers
@James skrip yang saya jalankan .connect () dari ada di server yang sama jika itu yang Anda maksud?
23
Metode terakhir tidak lagi valid , karena connectfungsi dipindahkan ke chrome.runtimenamespace. Lihat jawaban BJury (dan komentar) untuk versi yang lebih mutakhir
Xan
117

Chrome sekarang memiliki kemampuan untuk mengirim pesan dari situs web ke ekstensi.

Jadi dalam ekstensi background.js (content.js tidak akan berfungsi) tambahkan sesuatu seperti:

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

Ini kemudian akan memungkinkan Anda melakukan panggilan dari situs 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;
        }
    });

Anda kemudian dapat memeriksa variabel hasExtension. Satu-satunya kelemahan adalah panggilan itu tidak sinkron, jadi Anda harus mengatasinya entah bagaimana.

Edit: Seperti yang disebutkan di bawah ini, Anda harus menambahkan entri ke manifest.json yang mencantumkan domain yang dapat mengirim pesan ke addon Anda. Misalnya:

"externally_connectable": {
    "matches": ["*://localhost/*", "*://your.domain.com/*"]
},
BJury
sumber
2
Ini bekerja seperti pesona. Kekurangan lainnya adalah tentu saja Anda harus mengontrol ekstensi - Anda tidak dapat menggunakan ini untuk melihat apakah ekstensi pihak ketiga yang sewenang-wenang dipasang.
Eric P
2
@EricP Pertanyaan asli menyatakan bahwa mereka menulis ekstensi, jadi masalahnya bisa diperdebatkan.
BJury
13
Anda juga harus menambahkan berikut ini ke manifest.json Anda: "externally_connectable": {" matching ": [" : // .yourdomain.com / *"]}
noname
3
Seharusnya {versi: '1.0'} dan bukan {versi: 1.0} atau Anda akan mendapatkan 'Uncaught SyntaxError: Unexpected number' di ekstensi Periksa tampilan konsol.
ET-CS
1
Di sisi "pemeriksaan" (laman web mencoba memeriksa ketersediaan ekstensi yang diberikan), chrome.runtime tidak ditentukan, chrome 36 di linux.
reallynice
22

Metode lain adalah mengekspos sumber daya yang dapat diakses web , meskipun ini akan memungkinkan situs web apa pun untuk menguji apakah ekstensi Anda dipasang.

Misalkan ID ekstensi Anda adalah aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, dan Anda menambahkan file (katakanlah, gambar piksel transparan) seperti test.pngpada file ekstensi Anda.

Kemudian, Anda mengekspos file ini ke halaman web dengan web_accessible_resourceskunci manifes:

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

Di halaman web Anda, Anda dapat mencoba memuat file ini dengan URL lengkapnya (dalam <img>tag, melalui XHR, atau dengan cara lain):

chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/test.png

Jika file dimuat, ekstensi diinstal. Jika ada kesalahan saat memuat file ini, ekstensi belum terpasang.

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

Catatan: jika ada kesalahan saat memuat file ini, kesalahan tumpukan jaringan tersebut akan muncul di konsol tanpa kemungkinan untuk membungkamnya. Ketika Chromecast menggunakan metode ini, itu menyebabkan sedikit kontroversi karena ini; dengan solusi yang sangat buruk yaitu memasukkan kesalahan yang sangat spesifik dari Dev Tools ke daftar hitam sekaligus oleh tim Chrome.


Catatan penting: metode ini tidak akan berfungsi di Firefox WebExtensions. Sumber daya yang dapat diakses web secara inheren mengekspos ekstensi ke sidik jari, karena URL dapat diprediksi dengan mengetahui ID-nya. Firefox memutuskan untuk menutup lubang itu dengan menetapkan URL acak khusus instance ke sumber daya yang dapat diakses web:

File-file tersebut kemudian akan tersedia menggunakan URL seperti:

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

UUID ini dibuat secara acak untuk setiap browser dan bukan ID ekstensi Anda. Ini mencegah situs web mengambil sidik jari dari ekstensi yang telah dipasang pengguna.

Namun, meskipun ekstensi dapat digunakan runtime.getURL()untuk mendapatkan alamat ini, Anda tidak dapat melakukan hard-code di situs Anda.

Xan
sumber
Meskipun jawaban ini mendapatkan "jus" dari stackoverflow.com/a/9216924/1504300 , IMHO menambahkan beberapa informasi yang cukup penting seperti mengekspos sumber daya dalam ekstensi dan fakta bahwa Anda dapat menggunakan permintaan ajax untuk memeriksa keberadaan (Objek gambar sepertinya hanya tersedia di HTML5 jika saya tidak salah goo.gl/HBeI1i ). Dengan info dalam jawaban ini saya sudah bisa memecahkan masalah, saya menemukan itu seperti "keluar dari kotak" solusi
reallynice
@niconic Jawaban itu (bagaimanapun juga buruk sebagai link-only) mengacu pada situasi sebelum versi nyata 2 berlaku. Sebelumnya, seseorang tidak perlu menyatakan sumber daya dapat diakses web.
Xan
19

Saya pikir saya akan membagikan penelitian saya tentang ini. Saya harus dapat mendeteksi apakah ekstensi tertentu dipasang untuk beberapa file: /// tautan untuk bekerja. Saya menemukan artikel ini di sini. Ini menjelaskan metode untuk mendapatkan manifest.json dari sebuah ekstensi.

Saya menyesuaikan kodenya sedikit dan menghasilkan:

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

Dengan ini, Anda seharusnya dapat menggunakan Ext_Detect (ExtensionName, ExtensionID) untuk mendeteksi pemasangan sejumlah ekstensi.

NATO
sumber
2
Sepertinya Google membuat segalanya lebih aman, saya mendapatkan kesalahan berikut ketika saya menjalankan Ext_Detect (): Menolak pemuatan chrome-extension: // [my_extension_id] /manifest.json. Sumber daya harus dicantumkan dalam kunci manifes web_accessible_resources untuk dimuat oleh halaman di luar ekstensi.
Lounge9
Saya bisa mendapatkan ini untuk bekerja dengan Chrome Versi 32.0.1700.107 m mulai 27/2/2014
JE Carter II
1
seperti yang dikatakan @ Lounge9. Resource di dalam paket yang menggunakan manifest_version 2 (atau lebih tinggi) diblokir secara default , dan harus dimasukkan ke daftar putih untuk digunakan melalui properti ini dengan menambahkan ke manifest.json: "web_accessible_resources": ["manifest..json"],
ET-CS
Menggunakan jawaban @BJury Anda juga dapat dengan mudah mengirimkan data dari ekstensi ke skrip (misalnya versi ekstensi) dan Anda tidak perlu mengekspos file apa pun dari ekstensi.
ET-CS
1
Ini bekerja paling baik untuk saya karena ekstensi kami akan digunakan di beberapa domain dan tidak dapat ditentukan sebelumnya karena domain baru ditambahkan secara teratur. Alih-alih mengakses manifest.json saya membuat file baru version.json dan memasukkan nomor versi di dalamnya. Ini bekerja sama.
Paul Haggo
7

Solusi lain yang mungkin jika Anda memiliki situs web adalah menggunakan penginstalan inline .

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

Saya tahu ini pertanyaan lama tetapi cara ini diperkenalkan di Chrome 15 dan jadi saya pikir saya akan mencantumkannya untuk siapa saja yang sekarang mencari jawaban.

PAEz
sumber
12
Ini berfungsi dengan baik untuk Aplikasi Chrome , tetapi tidak untuk Ekstensi Chrome AFAIK
Eran Medan
Ya, situs web tersebut memberi tahu Anda cara memasang ekstensi secara sebaris , tetapi tampaknya merekomendasikan "Ekstensi dapat berkomunikasi dengan laman penyematan melalui skrip konten untuk memberi tahu bahwa mereka telah dipasang." alih-alih dapat menggunakan chrome.app.isInstalled.
Membingungkan
4

Saya menggunakan metode cookie:

Di file manifest.js saya, saya menyertakan skrip konten yang hanya berjalan di situs saya:

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

di js / mysite.js saya, saya memiliki satu baris:

document.cookie = "extension_downloaded=True";

dan di halaman index.html saya mencari cookie itu.

if (document.cookie.indexOf('extension_downloaded') != -1){
    document.getElementById('install-btn').style.display = 'none';
}
Chase Roberts
sumber
Saya mencoba semua solusi di atas tetapi tidak berhasil, lalu saya melihat jawaban Anda, inilah yang saya cari!
John Doe
Ini menambah overhead ke setiap permintaan HTTP selanjutnya.
mlissner
3

Anda dapat meminta ekstensi menyetel cookie dan meminta situs web Anda untuk memeriksa JavaScript apakah cookie itu ada dan memperbarui sebagaimana mestinya. Ini dan mungkin sebagian besar metode lain yang disebutkan di sini tentu saja dapat dibuat oleh pengguna, kecuali Anda mencoba dan meminta ekstensi membuat cookie khusus tergantung pada stempel waktu dll, dan meminta aplikasi Anda menganalisisnya di sisi server untuk melihat apakah itu benar-benar pengguna dengan ekstensi atau seseorang yang berpura-pura memilikinya dengan mengubah cookie-nya.

Niklas
sumber
5
satu-satunya masalah adalah jika pengguna menghapus ekstensi Anda. Cookie mungkin akan tetap disetel.
Chase Roberts
3

Ada metode lain yang ditampilkan di posting Google Grup ini . Singkatnya, Anda dapat mencoba mendeteksi apakah ikon ekstensi berhasil dimuat. Ini mungkin berguna jika ekstensi yang Anda periksa bukan milik Anda.

Pesolek
sumber
1
Baik. Bagaimana cara kami memeriksa apakah ada ikon ekstensi?
Michael Rogers
3

Halaman web berinteraksi dengan ekstensi melalui skrip latar belakang.

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
sumber
Tidak berfungsi di Firefox WebExtensions, jika externally_connectable tidak didukung.
mlissner
3

Ekstensi Anda dapat berinteraksi dengan situs web (mis. Mengubah variabel) dan situs web Anda dapat mendeteksinya.

Tetapi harus ada cara yang lebih baik untuk melakukan ini. Saya bertanya-tanya bagaimana Google melakukannya di galeri ekstensi mereka (aplikasi yang sudah diinstal ditandai).

Edit:

Galeri menggunakan fungsi chrome.management.get . Contoh:

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

Tetapi Anda hanya dapat mengakses metode dari halaman dengan izin yang tepat.

Fox32
sumber
1
Anda yang memerlukan ekstensi untuk berinteraksi dengan setiap situs di setiap tab yang akan lambat / sulit diterapkan dan
Masalahnya adalah komunikasi ke arah lain (halaman ke ekstensi) tidak dimungkinkan, karena model keamanan chrome. Jika Anda tidak ingin menggunakan cara 'berinteraksi', gunakan cara cookie.
Fox32
2
@ Fox32 yang terhormat, chrome.management.get ..., mengembalikan kesalahan ini:Uncaught TypeError: Cannot read property 'get' of undefined
Hosein Aqajani
3

Banyak jawaban di sini sejauh ini hanya untuk Chrome atau dikenakan penalti tambahan HTTP. Solusi yang kami gunakan sedikit berbeda:

1. Tambahkan objek baru ke daftar content_scripts manifes seperti ini:

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

Ini akan memungkinkan kode di install_notifier.js untuk berjalan di situs itu (jika Anda belum memiliki izin di sana).

2. Kirim pesan ke setiap situs dengan kunci manifes di atas.

Tambahkan sesuatu seperti ini ke install_notifier.js (perhatikan bahwa ini menggunakan closure untuk menjaga variabel agar tidak global, tetapi itu tidak sepenuhnya diperlukan):

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

Pesan Anda bisa mengatakan apa saja, tetapi berguna untuk mengirimkan versinya sehingga Anda tahu apa yang Anda hadapi. Kemudian...

3. Di situs web Anda, dengarkan pesan itu.

Tambahkan ini ke situs web Anda di suatu tempat:

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

Ini berfungsi di Firefox dan Chrome, dan tidak menimbulkan overhead HTTP atau memanipulasi halaman.

mlissner
sumber
0

Jika Anda memiliki kontrol atas ekstensi Chrome, Anda dapat mencoba apa yang saya lakukan:

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

Lalu:

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

}

Rasanya agak hack, tetapi saya tidak bisa mendapatkan metode lain untuk bekerja, dan saya khawatir Chrome mengubah API-nya di sini. Diragukan metode ini akan berhenti bekerja dalam waktu dekat.

gwg
sumber
seperti disebutkan di atas- Saya telah menguji ini tetapi urutan operasinya tampak aneh- skrip mana yang dijalankan lebih dulu, dll.?
Brady Moritz
0

Anda juga dapat menggunakan metode lintas-browser yang telah saya gunakan. Menggunakan konsep menambahkan div.

dalam skrip konten Anda (setiap kali skrip dimuat, itu harus melakukan ini)

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

di situs web Anda, Anda menyatakan sesuatu seperti,

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

Dan lemparkan pesan yang sesuai.

Prakash Palnati
sumber
Saya telah menguji ini tetapi urutan operasinya tampak aneh- skrip mana yang dijalankan lebih dulu, dll.?
Brady Moritz
@BradyMoritz urutan yang sama seperti di jawaban. Tambahkan kelas terlebih dahulu lalu tegaskan.
Prakash Palnati
namun sepertinya skrip konten saya tidak berjalan sebelum skrip dalam halaman saya?
Brady Moritz
Itu, menurut saya mudah untuk diperbaiki. Anda dapat memastikan bahwa skrip konten Anda berjalan tepat setelah mencapai URL / rute yang diperlukan menggunakan regex. Sudahkah Anda mencobanya?
Prakash Palnati
0

Jika Anda mencoba mendeteksi ekstensi apa pun dari situs web mana pun, Posting ini membantu: https://ide.hey.network/post/5c3b6c7aa7af38479accc0c7

Pada dasarnya, solusinya adalah dengan mencoba mendapatkan file tertentu (manifest.json atau gambar) dari ekstensi dengan menentukan jalurnya. Inilah yang saya gunakan. Pasti bekerja:

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)
}
menangkap.
sumber
0

Berikut adalah pendekatan modern lainnya:

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
sumber