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

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

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

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

19 ответов

Решение

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

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

Например:

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

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

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


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

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

Не проверено, но вы должны быть в состоянии сделать...

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

В 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/*"]
},

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

Предположим, что идентификатор вашего добавочного номера 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() чтобы получить этот адрес, вы не можете жестко закодировать его на своем веб-сайте.

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

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

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

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

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

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:

В мой файл 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';
}

Пока что здесь есть ответы только на 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-накладных расходов или манипулирования страницей.

Ваше расширение может взаимодействовать с сайтом (например, изменение переменных), и ваш сайт может обнаружить это.

Но должен быть лучший способ сделать это. Интересно, как Google это делает в своей галерее расширений (отмечены уже установленные приложения).

Редактировать:

Галерея использует функцию chrome.management.get. Пример:

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

Но вы можете получить доступ к методу только со страниц с правами доступа.

В этом сообщении групп Google показан другой метод. Короче говоря, вы можете попытаться определить, успешно ли загружается значок расширения. Это может быть полезно, если расширение, которое вы проверяете, не является вашим собственным.

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

Веб-страница взаимодействует с расширением через фоновый скрипт.

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>

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

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

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

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

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

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

Современный способ проверить наличие расширения, а также его можно использовать с Async/Await:

      $.sendMessage = (appId, request) => {
    return new Promise(resolve => {
    if (chrome.runtime)     // undefined if no extension allowed to message from this domain
        chrome.runtime.sendMessage(appId, request, response => {
            if (chrome.runtime.lastError) resolve()  // handle error because our extension's background script not there to answer
            resolve(response);  // extension background script answered
        });
    else
        resolve(); // extension not found
});

}

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

Сначала вам нужно открыть файл манифеста расширения, перейдя в 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>

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

Манифест расширения Chrome v3:

      const isFirefox = chrome.runtime.OnInstalledReason.CHROME_UPDATE != "chrome_update";

Для FireFox, я думаюchrome.runtime.OnInstalledReason.BROWSER_UPDATEбудет "browser_update": https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/OnInstalledReason

Если вы пытаетесь обнаружить какое-либо расширение с любого веб-сайта, это сообщение помогло: 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)
}

Я написал API, который будет считать установки для Chrome и Firefox. Вызывается этот API из или расширения. регистрbackground.jsвmanifest.jsonкак для хрома, так и для Firefox.

Я передаю имя браузера при вызове API.

Хром

      function handleInstalled(details) {
    if (details.reason == 'install') {
        fetch('https://YOUR_API_URL/api/browser-extension', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ browser: 'chrome' }),
        })
            .then((response) => response.json())
            .then((response) => console.log(response))
            .catch((error) => console.error(error))
    }
}

chrome.runtime.onInstalled.addListener(handleInstalled)

Fire Fox

      function handleInstalled(details) {
    if (details.reason == 'install') {
        fetch('https://dev.axisscambush.com/api/browser-extension', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ browser: 'firefox' }),
        })
            .then((response) => response.json())
            .then((response) => console.log(response))
            .catch((error) => console.error(error))
    }
}

browser.runtime.onInstalled.addListener(handleInstalled)
Другие вопросы по тегам