Перевірте, чи встановлено в користувача розширення Chrome


97

Я будую розширення Chrome, і щоб ця робота працювала так, як я хотіла, мені потрібен зовнішній скрипт JavaScript, щоб можна було визначити, чи встановлено у користувача моє розширення.

Наприклад: Користувач встановлює мій плагін, а потім переходить на веб-сайт із моїм сценарієм. Веб-сайт виявляє, що моє розширення встановлено, і відповідно оновлює сторінку.

Чи можливо це?


2
Так, розширення можна виявити, якщо ви знаєте свій ідентифікатор розширення (що я впевнений, що ви робите). Перегляньте цей сайт для отримання додаткової інформації: blog.kotowicz.net/2012/02/intro-to-chrome-addons-hacking.html Пропустіть до розділу "Пошук одних за одними адонів ". Удачі!
Мартін Хьюз,

Правильний спосіб реалізації цього описаний нижче БЮРІ.
Рахатур

ця публікація допомогла: ide.hey.network/post/5c3b6c7aa7af38479accc0c7
.

Відповіді:


46

Я впевнений, що існує прямий шлях (виклик функцій на вашому розширенні безпосередньо, або використання класів JS для розширень), але непрямий метод (поки щось краще не стане):

Нехай розширення Chrome шукає певний DIV чи інший елемент на вашій сторінці з дуже конкретним ідентифікатором.

Наприклад:

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

Виконайте a getElementByIdта встановіть innerHTMLномер версії вашого розширення або щось інше. Потім ви можете прочитати вміст цієї сторони клієнта.

Знову ж таки, вам слід використовувати прямий метод, якщо такий є.


EDIT: Прямий метод знайдено !!

Використовуйте методи підключення, знайдені тут: https://developer.chrome.com/extensions/extension#global-events

Неперевірений, але ви повинні зробити це ...

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

2
hmmm chrome.extension.connect, здається, працює лише тоді, коли виконується зсередини розширення (або будь-якого розширення). Мені це потрібно для роботи з будь-якого випадкового сценарію js. Якісь ідеї?

Як не дивно, документація говорить, що вона повинна працювати. « В відміну від інших хрому. * APIs, частини chrome.extension можуть бути використані скрипти контенту» і це списки sendRequest(), onRequest, connect(), onRequest, і getURL().
Бред

@James, ти виконуєш сценарій, який використовує .connect () із розміщеного сценарію? Я знаю, що Chrome намагається не робити речі лише з локальними файлами, які не розміщуються в цілях безпеки. - Просто перевіряю.
JamesEggers

@James скрипт, з якого я виконую .connect (), знаходиться на тому ж сервері, якщо це ви маєте на увазі?

23
Останній метод вже не дійсний , оскільки connectфункцію було переміщено до chrome.runtimeпростору імен. Дивіться відповідь (та коментарі) БЮрі для більш оновленої версії
Xan

117

Зараз у 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/*"]
},

2
Це працює як шарм. Ще один недолік - це, звичайно, що ви маєте керувати розширенням - ви не можете використовувати це, щоб побачити, чи встановлено довільне розширення сторонніх розробників.
Ерік П

2
@EricP В оригінальному запитанні було зазначено, що вони писали розширення, тому проблема спірна.
Журі

13
Вам також доведеться додати в свій manifest.json наступне: "externally_connectable": {" match ": [" : // .yourdomain.com / *"]}
noname

3
Це має бути {версія: '1.0'}, а не {версія: 1.0}, інакше ви отримаєте "Uncaught SyntaxError: Несподіваний номер" у розширенні Inspect view console.
ET-CS

1
На стороні "перевірка" (веб-сторінка, яка намагається перевірити наявність певного розширення), chrome.runtime не визначено, chrome 36 у Linux.
reallynice

22

Інший метод - відкрити доступний веб-ресурс , хоча це дозволить будь-якому веб-сайту перевірити, чи встановлено розширення.

Припустимо, ідентифікатор розширення є 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 використовував цей метод, він викликав досить багато суперечок через це; з можливим дуже потворним рішенням - просто внести до чорного списку дуже специфічні помилки від Dev Tools командою Chrome.


Важлива примітка: цей метод не працюватиме в Firefox WebExtensions. Доступні в Інтернеті ресурси за своєю суттю піддають розширення дактилоскопії, оскільки URL-адресу можна передбачити, знаючи ідентифікатор. Firefox вирішив закрити цю дірку, призначивши випадкову URL-адресу для конкретного примірника веб-доступним ресурсам:

Файли будуть доступні за допомогою такої URL-адреси, як:

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

Цей UUID генерується випадковим чином для кожного екземпляра браузера і не є ідентифікатором вашого розширення. Це заважає веб-сайтам дактилоскопіювати розширення, встановлені користувачем.

Однак, хоча розширення може використовуватись runtime.getURL()для отримання цієї адреси, ви не можете жорстко закодувати його на своєму веб-сайті.


Хоча ця відповідь отримує "сік" від stackoverflow.com/a/9216924/1504300 , IMHO додає деякі досить важливі відомості, такі як відкриття ресурсу в розширенні та факт, що ви можете використовувати запит ajax для перевірки наявності (об’єкт зображення здається доступним лише в HTML5, якщо я не помиляюсь goo.gl/HBeI1i ). З інформацією в цій відповіді я зміг вирішити проблему, я знайшов це як "нестандартне" рішення
дійсно,

@niconic Ця відповідь (будучи поганою як лише посилання) стосується ситуації до набрання чинності версії 2. Раніше не потрібно було оголошувати ресурси доступними через Інтернет.
Xan

19

Я думав, що поділюсь своїми дослідженнями з цього приводу. Мені потрібно було виявити, чи встановлено певне розширення для того, щоб якийсь файл: /// посилання працював. Я зіткнувся з цією статтею тут. Це пояснило метод отримання 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) для виявлення установки будь-якої кількості розширень.


2
Схоже, Google зробив речі більш безпечними, при запуску Ext_Detect () я отримую таку помилку: Заборона завантаження chrome-extension: // [my_extension_id] /manifest.json. Ресурси повинні бути перелічені в ключі маніфесту web_accessible_resources, щоб завантажуватися сторінками поза розширенням.
Lounge9

Я можу змусити це працювати з версією 32.0.1700.107 m Chrome станом на 27.02.2014
JE Carter II

1
як сказав @ Lounge9. Ресурси всередині пакетів, що використовують manifest_version 2 (або вище), за замовчуванням блокуються і повинні бути допущені до списку для використання через цю властивість, додавши до manifest.json: "web_accessible_resources": ["manifest..json"],
ET-CS

Використовуючи @BJury відповідь, ви також можете легко передавати дані з розширення до сценарію (наприклад, версія розширення), і вам не потрібно виставляти жодного файлу з розширення.
ET-CS

1
Це найкраще мені вдалося, оскільки наше розширення буде використовуватися в кількох доменах і не може бути попередньо визначеним, оскільки нові домени регулярно додаються. Замість доступу до manifest.json я створив новий файл version.json і помістив номер версії всередину. Це працює точно так само.
Пол Хагго

7

Ще одним можливим рішенням, якщо ви володієте веб-сайтом, є використання вбудованої установки .

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

Я знаю це давнє запитання, але цей спосіб був представлений у Chrome 15, і тому я думав, що перелічу його для тих, хто тільки зараз шукає відповіді.


12
Це чудово підходить для додатка Chrome , але не для розширення для Chrome AFAIK
Eran Medan

Так, цей веб-сайт розповідає вам про те, як вбудувати інсталяцію розширення, але, мабуть, рекомендує "Розширення можуть спілкуватися зі вбудовуваною сторінкою за допомогою скриптів вмісту, щоб вони могли знати, що вони вже встановлені". замість можливості використовувати chrome.app.isInstalled.
Збентежив і


4

Я використовував метод 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 я шукаю це печиво.

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

Я спробував усе рішення вище, але не працює, тоді я бачу вашу відповідь, це те, що я шукаю!
John Doe

Це додає накладні витрати на кожен запит HTTP відтепер.
mlissner

3

Ви можете встановити для розширення файл cookie, а ваш веб-сайт перевірити JavaScript, чи є цей файл cookie, і відповідно оновити. Цей і, мабуть, більшість інших методів, згаданих тут, може, звичайно, бути змінено користувачем, якщо ви не спробуєте і не розширення створить власні файли cookie в залежності від позначок часу тощо, і не попросите ваш додаток проаналізувати їх на стороні сервера, щоб перевірити, чи дійсно це користувач із розширення або хтось вдає, що має його, модифікуючи свої файли cookie.


5
проблема полягає лише в тому, що якщо користувач видаляє ваші розширення. Печиво, ймовірно, залишиться встановленим.
Чейз Робертс

3

У цій публікації Групи Google показаний інший спосіб . Якщо коротко, ви можете спробувати визначити, чи успішно завантажується значок розширення. Це може бути корисно, якщо розширення, яке ви перевіряєте, не є вашим власним.


1
Гаразд. Як перевірити, чи існує піктограма розширення?
Майкл Роджерс

3

Веб-сторінка взаємодіє з розширенням за допомогою фонового сценарію.

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>

Не працює у Firefox WebExtensions, де external_connectable не підтримується.
mlissner

3

Ваше розширення може взаємодіяти з веб-сайтом (наприклад, зміною змінних), і ваш веб-сайт може це виявити.

Але для цього повинен бути кращий спосіб. Цікаво, як Google робить це у своїй галереї розширень (уже встановлені програми позначені).

Редагувати:

Галерея використовує функцію chrome.management.get . Приклад:

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

Але ви можете отримати доступ до методу лише зі сторінок з відповідними дозволами.


1
ви потребуєте розширення для взаємодії з кожним сайтом на кожній вкладці, який буде повільним / важким для впровадження та баггі: - /

Проблема полягає в тому, що спілкування в іншому напрямку (від сторінки до розширення) неможливе через модель безпеки chrome. Якщо ви не хочете йти `` взаємодіючим '' шляхом, скористайтеся файлом cookie.
Fox32

2
Шановний @ Fox32, chrome.management.get ..., повертає цю помилку:Uncaught TypeError: Cannot read property 'get' of undefined
Hosein Aqajani

3

Багато відповідей тут поки що стосуються лише Chrome або вимагають HTTP-штрафу. Ми використовуємо рішення дещо інше:

1. Додайте новий об’єкт до списку маніфесту content_scripts таким чином:

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

Це дозволить коду в install_notifier.js запускатися на цьому веб-сайті (якщо ви ще не мали там дозволів).

2. Надішліть повідомлення на кожен сайт у маніфестному ключі вище.

Додайте щось подібне до 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
  }, "*");
})();

Ваше повідомлення може говорити що завгодно, але корисно надіслати версію, щоб ви знали, з чим маєте справу. Тоді...

3. На своєму веб-сайті прислухайтеся до цього повідомлення.

Додайте це десь на свій веб-сайт:

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 накладні витрати та не маніпулює сторінкою.


0

Якщо у вас є контроль над розширенням 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. Сумнівно, що цей метод незабаром перестане працювати.


як вже згадувалося вище - я це перевірив, але порядок операцій видається дивним - який скрипт запускається першим і т.д.?
Брейді Моріц

0

Ви також можете використовувати метод перехресного перегляду, який я використовував. Використовується концепція додавання діл.

у вашому сценарії вмісту (коли скрипт завантажується, він повинен це зробити)

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

на своєму веб-сайті ви стверджуєте щось подібне,

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

І киньте відповідне повідомлення.


Я тестував це, але порядок операцій здається дивним - який скрипт запускається першим і т.д.?
Брейді Моріц

@BradyMoritz той самий порядок, що і у відповіді. Спершу додайте клас, а потім затвердіть.
Prakash Palnati

здавалося, що мій скрипт вмісту не обов’язково працює перед моїм сценарієм на сторінці?
Брейді Моріц

Це, я думаю, легко виправити. Ви можете переконатись, що ваш сценарій вмісту працює відразу після натискання потрібної URL-адреси / маршруту за допомогою regex. ви пробували це?
Prakash Palnati

0

Якщо ви намагаєтеся виявити будь-яке розширення з будь-якого веб-сайту, ця публікація допомогла: 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)
}

0

Ось інший сучасний підхід:

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')
})
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.