Проверьте, установлено ли у пользователя расширение Chrome


100

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

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

Это возможно?


2
Да, можно обнаружить расширения, если вы знаете свой идентификатор расширения (я уверен, что вы знаете). Посетите этот сайт для получения дополнительной информации: blog.kotowicz.net/2012/02/intro-to-chrome-addons-hacking.html Перейдите к разделу «Поиск ваших надстроек по одному». Удачи!
— Мартин Хьюз

Правильный способ реализации этого описан ниже BJury.
— Rahatur

Ответы:


47

Я уверен, что существует прямой способ (вызов функций вашего расширения напрямую или с использованием классов JS для расширений), но косвенный метод (пока не появится что-то лучшее):

Попросите расширение Chrome искать определенный DIV или другой элемент на вашей странице с очень конкретным идентификатором.

Например:

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

Сделайте getElementByIdи установите innerHTMLномер версии вашего расширения или что-то в этом роде. Затем вы можете прочитать содержимое этой клиентской стороны.

Опять же, вы должны использовать прямой метод, если он доступен.


РЕДАКТИРОВАТЬ: найден прямой метод !!

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

Не проверено, но вы должны уметь ...

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

3
хммм chrome.extension.connect, похоже, работает только при выполнении из расширения (или любого расширения). Мне нужно, чтобы он работал с любым случайным скриптом js. Любые идеи?

Странно, но в документации написано, что это должно работать. « В отличие от других хрома. * APIs, части chrome.extension могут быть использованы скрипты контента» и это списки sendRequest(), onRequest, connect(), onRequest, и getURL().
— Брэд

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

@James сценарий, с которого я выполняю .connect (), находится на том же сервере, если вы это имеете в виду?

23
Последний метод больше не действителен , так как connectфункция была перемещена в chrome.runtimeпространство имен. См. Ответ (и комментарии) BJury для получения более свежей версии
— Xan

122

В 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
Это работает как шарм. Другой недостаток, конечно, заключается в том, что вам нужно управлять расширением - вы не можете использовать это, чтобы увидеть, установлено ли произвольное стороннее расширение.
— Eric P

2
@EricP В исходном вопросе говорилось, что они пишут расширение, поэтому вопрос спорный.
— BJury

13
Вам также нужно будет добавить в свой manifest.json следующее: "external_connectable": {" matches ": [" : // .yourdomain.com / *"]}
— noname

3
Это должно быть {version: '1.0'}, а не {version: 1.0}, иначе вы получите сообщение «Uncaught SyntaxError: Unexpected number» в консоли просмотра расширения.
— ET-CS

1
На стороне «проверки» (веб-страница пытается проверить доступность данного расширения) chrome.runtime не определен, chrome 36 в Linux.
— reallynice

22

Другой метод - предоставить доступ к веб-ресурсу , хотя это позволит любому веб-сайту проверить, установлено ли ваше расширение.

Предположим, у вашего расширения есть ID 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

Я думал, что поделюсь своим исследованием по этому поводу. Мне нужно было определить, было ли установлено конкретное расширение для работы некоторых ссылок file: ///. Я наткнулся на эту статью здесь. В ней объясняется метод получения 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) для обнаружения установки любого количества расширений.


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

Я могу заставить это работать с версией 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 App , но не для Chrome Extension AFAIK
— Эран Медан

Да что сайт действительно говорит вам , как рядный установить расширение, но , видимо , рекомендует «Расширения могут взаимодействовать с вложением страницы с помощью сценариев контента , чтобы сообщить ему , что они уже установлены.» вместо возможности использовать 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 я ищу этот файл cookie.

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

Я пробовал все вышеперечисленные решения, но не работает, тогда я вижу ваш ответ, это то, что я ищу!
— Джон Доу

Отныне это увеличивает накладные расходы на каждый HTTP-запрос.
— mlissner

3

Вы можете настроить расширение для файлов cookie, а JavaScript на ваших веб-сайтах будет проверять наличие этого файла cookie и обновлять его соответствующим образом. Этот и, вероятно, большинство других упомянутых здесь методов, конечно, могут быть обойдены пользователем, если только вы не попытаетесь заставить расширение создавать пользовательские файлы cookie в зависимости от временных меток и т. Д., И пусть ваше приложение проанализирует их на стороне сервера, чтобы увидеть, действительно ли это пользователь с расширение или кто-то, претендующий на его наличие, изменив свои файлы cookie.


5
единственная проблема в том, что если пользователь удалит ваши расширения. Файл cookie, вероятно, останется установленным.
— Чейз Робертс,

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 09

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 или манипулирования страницей.


2

Вот еще один современный подход:

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

1
обязательно обновите манифест, чтобы разрешить доступ к ресурсам: "web_accessible_resources": ["icons / *. png"]
— Дерек Уэйд

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

Вы также можете использовать кроссбраузерный метод, который использовал я. Использует концепцию добавления div.

в вашем сценарии содержимого (всякий раз, когда сценарий загружается, он должен это делать)

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

на своем веб-сайте вы утверждаете что-то вроде,

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

И закинуть соответствующее сообщение.


Я тестировал это, но порядок операций кажется странным - какой сценарий запускается первым и т. Д.?
— Брэди Мориц

@BradyMoritz в том же порядке, что и в ответе. Сначала добавьте класс, а затем подтвердите.
— Prakash Palnati 01

похоже, что мой сценарий содержимого не обязательно запускался до моего встроенного сценария?
— Брэди Мориц

Думаю, это легко исправить. Вы можете убедиться, что ваш контентный скрипт запускается сразу после нажатия необходимого 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

Вот как вы можете обнаружить конкретное установленное расширение и показать предупреждающее сообщение.

Сначала вам нужно открыть файл манифеста расширения, перейдя в chrome-extension: //extension_id_here_hkdppipefbchgpohn/manifest.json и найдите любое имя файла в разделе «web_accessible_resources».

<div class="chromewarning" style="display:none">
    <script type="text/javascript">
                            $.get("chrome-extension://extension_id_here_hkdppipefbchgpohn/filename_found_in_ web_accessible_resources.png").done(function () {
                              $(".chromewarning").show();
                            }).fail(function () {
                             //  alert("failed.");
                            });
                        </script>
                        <p>We have detected a browser extension that conflicts with learning modules in this course.</p>
            </div>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.