Проверьте, установлено ли у пользователя расширение 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.