Ответ 1
Ваш script не работает из-за того, как вставляются скрипты содержимого.
Проблема
Когда вы (повторно) загружаете свое расширение, вопреки ожиданиям некоторых людей, Chrome не будет добавлять сценарии содержания в существующие вкладки, которые соответствуют шаблонам из манифеста. Только после того, как расширение будет загружено, любая навигация будет проверять URL-адрес для сопоставления и будет вводить код.
Итак, временная шкала:
- Вы открываете некоторые вкладки. Нет сценариев содержания 1.
- Вы загружаете расширение. Выполняется его код верхнего уровня: он пытается передать сообщение на текущую вкладку.
- Так как там еще нет слушателя, он терпит неудачу. (Это, вероятно, страница
chrome://extensions/
, и вы все равно не можете вставлять туда) - Если после этого вы попытаетесь перейти/открыть новую вкладку, слушатель будет введен, но ваш код верхнего уровня больше не будет выполнен.
1 - Это также происходит, если вы перезагрузите расширение. Если был добавлен контент script, он продолжает обрабатывать свои события/не выгружается, но больше не может связываться с расширением. (подробности см. в дополнении в конце)
Решение
Решение 1: вы можете сначала запросить вкладку, по которой вы отправляете сообщение, готово ли оно, и после молчания введите программный код script. Рассмотрим:
// Background
function ensureSendMessage(tabId, message, callback){
chrome.tabs.sendMessage(tabId, {ping: true}, function(response){
if(response && response.pong) { // Content script ready
chrome.tabs.sendMessage(tabId, message, callback);
} else { // No listener on the other end
chrome.tabs.executeScript(tabId, {file: "content_script.js"}, function(){
if(chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
throw Error("Unable to inject script into tab " + tabId);
}
// OK, now it injected and ready
chrome.tabs.sendMessage(tabId, message, callback);
});
}
});
}
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
ensureSendMessage(tabs[0].id, {greeting: "hello"});
});
и
// Content script
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if(request.ping) { sendResponse({pong: true}); return; }
/* Content script action */
});
Решение 2: всегда вводит script, но убедитесь, что он выполняется только один раз.
// Background
function ensureSendMessage(tabId, message, callback){
chrome.tabs.executeScript(tabId, {file: "content_script.js"}, function(){
if(chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
throw Error("Unable to inject script into tab " + tabId);
}
// OK, now it injected and ready
chrome.tabs.sendMessage(tabId, message, callback);
});
}
и
// Content script
var injected;
if(!injected){
injected = true;
/* your toplevel code */
}
Это проще, но имеет сложности с перезагрузкой. После того, как расширение будет перезагружено, старый script все еще существует 1 но это не "ваш" контекст больше, поэтому injected
будет undefined. Остерегайтесь побочных эффектов потенциального выполнения вашего script дважды.
Решение 3: просто без разницы вводит ваш контент script (s) при инициализации. Это безопасно, если можно безопасно запускать один и тот же контент script дважды, или запустить его после полной загрузки страницы.
chrome.tabs.query({}, function(tabs) {
for(var i in tabs) {
// Filter by url if needed; that would require "tabs" permission
// Note that injection will simply fail for tabs that you don't have permissions for
chrome.tabs.executeScript(tabs[i].id, {file: "content_script.js"}, function() {
// Now you can use normal messaging
});
}
});
Я также подозреваю, что вы хотите, чтобы он работал на каком-то мероприятии, а не на нагрузке расширения. Например, вы можете использовать Действие браузера и обернуть свой код в прослушиватель chrome.browserAction.onClicked
.
Добавление к сценариям с сиротским контентом
Когда расширение будет перезагружено, можно ожидать, что Chrome очистит все скрипты содержимого. Но, по-видимому, это не так; слушатели контент-скриптов не отключены. Тем не менее, любая передача сообщений с родительским расширением не будет выполнена. Это, вероятно, следует считать ошибкой и в какой-то момент можно исправить. Я собираюсь назвать это состояние "осиротевшим"
Это не проблема ни в одном из двух случаев:
- Содержимое script не имеет прослушивателей для событий на странице (например, выполняется только один раз или только прослушивает сообщения из фона)
- Содержимое script ничего не делает со страницей и только сообщения о событиях.
Однако, если это не так, у вас возникла проблема: контент script может что-то делать, но не работает или не вмешивается в другой, сам по себе несигналированный экземпляр.
Решение этого будет:
- Следите за всеми прослушивателями событий, которые могут запускаться на странице
- Прежде чем действовать на эти события, отправьте сообщение "heartbeat" на задний план. 3a. Если фон отвечает, мы хороши и должны выполнить действие. 3b. Если передача сообщения завершается с ошибкой, мы становимся сиротами и должны воздерживаться; игнорировать событие и отменять регистрацию всех слушателей.
Код, содержимое script:
function heartbeat(success, failure) {
chrome.runtime.sendMessage({heartbeat: true}, function(reply){
if(chrome.runtime.lastError){
failure();
} else {
success();
}
});
}
function handler() {
heartbeat(
function(){ // hearbeat success
/* Do stuff */
},
function(){ // hearbeat failure
someEvent.removeListener(handler);
console.log("Goodbye, cruel world!");
}
);
}
someEvent.addListener(handler);
Фон script:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if(request.heartbeat) { sendResponse(request); return; }
/* ... */
});