Передайте переменную из скрипта контента во всплывающее окно

Я возиться (пытаясь научиться), как сделать расширение Chrome. Прямо сейчас я просто делаю супер простой, где он считает экземпляры определенного слова на странице. У меня эта часть работает.

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

Вот что у меня так далеко:

manifest.json

{
    "manifest_version": 2,
    "name": "WeedKiller",
    "description": "Totally serious $100% legit extension",
    "version": "0.1",

    "background": {
        "persistent": false,
        "scripts": ["background.js"]
    },

    "permissions":[
        "tabs",
        "storage"
    ],
    "browser_action": {
    "default_icon": "icon.png",
    "default_title": "WeedKiller",
    "default_popup": "popup.html"
    },
    "content_scripts": [
        {
            "matches": [
                "http://*/*",
                "https://*/*"
            ],
            "js": [
                "content.js"
            ],
            "run_at": "document_end"
        }
    ]
}

content.js

var elements = document.getElementsByTagName('*');
var count = 0;

function tokeCounter(){
    for (var i = 0; i < elements.length; i++) {
        var element = elements[i];

        for (var j = 0; j < element.childNodes.length; j++) {
            var node = element.childNodes[j];

            if (node.nodeType === 3) {
                var text = node.nodeValue;
                if(text == '420'){
                    count++; 
                }

                var replacedText = text.replace(/420/, '+1');

                if (replacedText !== text) {
                    element.replaceChild(document.createTextNode(replacedText), node);
                }
            }
        }    
    }
}

tokeCounter();

Так что я хочу, чтобы отправить count переменная для всплывающего окна, чтобы я мог использовать его там.

Я посмотрел вокруг и обнаружил, что мне нужно что-то делать с chrome.runtime.sendMessage,

У меня есть, поэтому я добавляю эту строку в конец content.js:

 chrome.runtime.sendMessage(count);

а затем в background.js:

chrome.runtime.onMessage.addListener(
    function(response, sender, sendResponse){      
       temp = response;
    }
);

Я застрял здесь, потому что я не уверен, как отправить эту информацию всплывающему окну и использовать ее.

1 ответ

Решение

Как вы правильно заметили, вы не можете отправлять данные непосредственно во всплывающее окно, когда оно закрыто. Итак, вы отправляете данные на фоновую страницу.

Затем, когда вы открываете всплывающее окно, вы хотите данные там. Итак, какие есть варианты?

Обратите внимание: этот ответ сначала даст плохой совет, а затем улучшит его. Поскольку OP учится, важно показать мыслительный процесс и дорожные препятствия.


Первое решение, которое приходит на ум, заключается в следующем: спросите фоновую страницу, снова используя Messaging. Раннее предупреждение: это не будет работать или работать плохо

Прежде всего, установите, что могут быть различные типы сообщений. Изменение вашего текущего кода сообщений:

// content.js
chrome.runtime.sendMessage({type: "setCount", count: count});

// background.js
chrome.runtime.onMessage.addListener(
    function(message, sender, sendResponse) {
        switch(message.type) {
            case "setCount":
                temp = message.count;
                break;
            default:
                console.error("Unrecognised message: ", message);
        }
    }
);

А теперь вы можете теоретически спросить это во всплывающем окне:

// popup.js
chrome.runtime.sendMessage({type: "getCount"}, function(count) {
    if(typeof count == "undefined") {
        // That's kind of bad
    } else {
        // Use count
    }
});

// background.js
chrome.runtime.onMessage.addListener(
    function(message, sender, sendResponse) {
        switch(message.type) {
            case "setCount":
                temp = message.count;
                break;
            case "getCount":
                sendResponse(temp);
                break;
            default:
                console.error("Unrecognised message: ", message);
        }
    }
);

Теперь, какие проблемы с этим?

  1. Какова продолжительность жизни temp? Вы прямо заявили "persistent": false в вашем манифесте. В результате фоновая страница может быть выгружена в любое время, стирая состояние, такое как temp,

    Вы можете исправить это с "persistent": true, но продолжайте читать.

  2. Какую вкладку вы ожидаете увидеть? temp в него будут записаны последние данные, которые могут не совпадать с текущей вкладкой.

    Вы можете исправить это, сохранив вкладки (посмотрите, что я там делал?), На какую вкладку отправлялись данные, например, используя:

    // background.js
    /* ... */
      case "setCount":
          temp[sender.tab.id] = message.count;
          break;
      case "getCount":
          sendResponse(temp[message.id]);
          break;
    
    // popup.js
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        // tabs is a single-element array after this filtering
        chrome.runtime.sendMessage({type: "getCount", id: tabs[0].id}, function(count) {
            /* ... */
        });
    });
    

    Это много работы, правда? Это решение отлично работает, хотя для данных, не относящихся к вкладкам, после исправления 1.


Следующее улучшение, которое стоит рассмотреть: нужна ли нам фоновая страница, чтобы сохранить результат для нас? В конце концов, chrome.storage это вещь; это постоянное хранилище, к которому могут обращаться все сценарии расширения (включая сценарии содержимого).

Это вырезает фон (и сообщения) из картинки:

// content.js
chrome.storage.local.set({count: count});

// popup.js
chrome.storage.local.get("count", function(data) {
    if(typeof data.count == "undefined") {
        // That's kind of bad
    } else {
        // Use data.count
    }
});

Это выглядит чище, и полностью обходит проблему 1 сверху, но проблема 2 становится сложнее. Вы не можете напрямую установить / прочитать что-то вроде count[id] в хранилище, вам нужно будет прочитать count изменить, и написать его обратно. Это может стать медленным и грязным.

Добавьте к этому, что скрипты контента на самом деле не знают своего идентификатора вкладки; Вы должны будете отправить фон, чтобы узнать это. Тьфу. Не красиво Опять же, это отличное решение для данных, не относящихся к табуляциям.


Тогда следующий вопрос, который нужно задать: зачем нам даже центральное место для хранения результата (специфичного для табуляции)? Время жизни скрипта контента - это время жизни страницы. Вы можете запросить контент скрипта напрямую в любой момент. В том числе из всплывающего окна.

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

Итак, давайте изменим логику скрипта контента. Вместо немедленной отправки данных, подождите и прослушайте запросы:

chrome.runtime.onMessage.addListener(
    function(message, sender, sendResponse) {
        switch(message.type) {
            case "getCount":
                sendResponse(count);
                break;
            default:
                console.error("Unrecognised message: ", message);
        }
    }
);

Затем во всплывающем окне нам нужно запросить вкладку, которая содержит скрипт содержимого. Это другая функция обмена сообщениями, и мы должны указать идентификатор вкладки.

    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
        chrome.tabs.sendMessage(tabs[0].id, {type: "getCount"}, function(count) {
            /* ... */
        });
    });

Теперь это намного чище. Проблема 2 решена: мы запрашиваем вкладку, от которой хотим услышать. Кажется, проблема 1 решена: пока скрипт считает то, что нам нужно, он может ответить.

Обратите внимание, что в качестве последнего осложнения, скрипты содержимого не всегда внедряются, когда вы ожидаете их: они начинают активироваться при навигации только после (повторной) загрузки расширения. Вот ответ, объясняющий это очень подробно. При желании его можно обойти, но пока просто путь к нему:

function(count) {
    if(typeof count == "undefined") {
        // That's kind of bad
        if(chrome.runtime.lastError) {
            // We couldn't talk to the content script, probably it's not there
        }
    } else {
        // Use count
    }
}
Другие вопросы по тегам