Extensão do Chrome como enviar dados do script de conteúdo para popup.html

95

Eu sei que isso foi perguntado em vários posts, mas honestamente não os entendi. Eu sou novo em JavaScript, extensões do Chrome e tudo mais e tenho este trabalho de aula. Portanto, preciso fazer um plug-in que conte os objetos DOM em qualquer página usando Solicitações de vários domínios. Consegui fazer isso até agora usando a API de extensão do Chrome. Agora, o problema é que preciso mostrar os dados na minha página popup.html do arquivo contentScript.js. Não sei como fazer isso. Tentei ler a documentação, mas mensagens no Chrome, simplesmente não consigo entender o que fazer.

a seguir está o código até agora.

manifest.json

{
"manifest_version":2,

"name":"Dom Reader",
"description":"Counts Dom Objects",
"version":"1.0",

"page_action": {
    "default_icon":"icon.png",
    "default_title":"Dom Reader",
    "default_popup":"popup.html"
},

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

"content_scripts":[
    {
        "matches":["http://pluralsight.com/training/Courses/*", "http://pluralsight.com/training/Authors/Details/*",                                          "https://www.youtube.com/user/*", "https://sites.google.com/site/*", "http://127.0.0.1:3667/popup.html"],
        "js":["domReader_cs.js","jquery-1.10.2.js"]
        //"css":["pluralsight_cs.css"]
    }
],

"permissions":[
    "tabs",
    "http://pluralsight.com/*",
    "http://youtube.com/*",
    "https://sites.google.com/*",
    "http://127.0.0.1:3667/*"
]

popup.html

<!doctype html>
<html>

    <title> Dom Reader </title>    
    <script src="jquery-1.10.2.js" type="text/javascript"></script>
    <script src="popup.js" type="text/javascript"></script>

<body>
    <H1> Dom Reader </H1>
    <input type="submit" id="readDom" value="Read DOM Objects" />

   <div id="domInfo">

    </div>
</body>
</html>

eventPage.js

var value1,value2,value3;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action == "show") {
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        chrome.pageAction.show(tabs[0].id);
    });
}

value1 = request.tElements;
});

popup.js

$(function (){
$('#readDom').click(function(){
chrome.tabs.query({active: true, currentWindow: true}, function (tabs){
    chrome.tabs.sendMessage(tabs[0].id, {action: "readDom"});

 });
});
});

contentScript

var totalElements;
var inputFields;
var buttonElement;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse){
if(request.action == "readDom"){

    totalElements = $("*").length;
    inputFields = $("input").length;
    buttonElement = $("button").length;


}
})

chrome.runtime.sendMessage({ 
action: "show", 
tElements: totalElements, 
Ifields: inputFields, 
bElements: buttonElement 

});

Qualquer ajuda seria apreciada e por favor, evite qualquer noobness que eu fiz :)

Sumair Baloch
fonte

Respostas:

171

Embora você esteja definitivamente na direção certa (e na verdade bem perto do fim), existem várias (imo) práticas ruins em seu código (por exemplo, injetar uma biblioteca inteira (jquery) para uma tarefa tão trivial, declarar permissões desnecessárias, tornando supérfluo chamadas para métodos de API, etc.).
Eu não testei seu código, mas de uma rápida visão geral, acredito que corrigir o seguinte poderia resultar em uma solução de trabalho (embora não muito perto do ideal):

  1. Em manifest.json : Altere a ordem dos scripts de conteúdo, colocando jquery em primeiro lugar. De acordo com os documentos relevantes :

    "js" [...] A lista de arquivos JavaScript a serem injetados nas páginas correspondentes. Eles são injetados na ordem em que aparecem nesta matriz.

    (ênfase minha)

  2. Em contentscript.js : Mova o chrome.runtime.sendMessage ({...}) bloco dentro do onMessagecallback ouvinte.


Dito isso, aqui está minha abordagem proposta:

Controle de fluxo:

  1. Um script de conteúdo é injetado em cada página que corresponde a alguns critérios.
  2. Uma vez injetados, os scripts de conteúdo enviam uma mensagem para a página do evento (também conhecida como página de fundo não persistente) e a página do evento anexa uma ação de página à guia.
  3. Assim que o pop-up de ação da página é carregado, ele envia uma mensagem ao script de conteúdo, pedindo as informações de que precisa.
  4. O script de conteúdo processa a solicitação e responde para que o pop-up de ação da página exiba as informações.

Estrutura do diretório:

          root-directory/
           |_____img
                 |_____icon19.png
                 |_____icon38.png
           |_____manifest.json
           |_____background.js
           |_____content.js
           |_____popup.js
           |_____popup.html

manifest.json:

{
  "manifest_version": 2,
  "name": "Test Extension",
  "version": "0.0",
  "offline_enabled": true,

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

  "content_scripts": [{
    "matches": ["*://*.stackoverflow.com/*"],
    "js": ["content.js"],
    "run_at": "document_idle",
    "all_frames": false
  }],

  "page_action": {
    "default_title": "Test Extension",
    //"default_icon": {
    //  "19": "img/icon19.png",
    //  "38": "img/icon38.png"
    //},
    "default_popup": "popup.html"
  }

  // No special permissions required...
  //"permissions": []
}

background.js:

chrome.runtime.onMessage.addListener((msg, sender) => {
  // First, validate the message's structure.
  if ((msg.from === 'content') && (msg.subject === 'showPageAction')) {
    // Enable the page-action for the requesting tab.
    chrome.pageAction.show(sender.tab.id);
  }
});

content.js:

// Inform the background page that 
// this tab should have a page-action.
chrome.runtime.sendMessage({
  from: 'content',
  subject: 'showPageAction',
});

// Listen for messages from the popup.
chrome.runtime.onMessage.addListener((msg, sender, response) => {
  // First, validate the message's structure.
  if ((msg.from === 'popup') && (msg.subject === 'DOMInfo')) {
    // Collect the necessary data. 
    // (For your specific requirements `document.querySelectorAll(...)`
    //  should be equivalent to jquery's `$(...)`.)
    var domInfo = {
      total: document.querySelectorAll('*').length,
      inputs: document.querySelectorAll('input').length,
      buttons: document.querySelectorAll('button').length,
    };

    // Directly respond to the sender (popup), 
    // through the specified callback.
    response(domInfo);
  }
});

popup.js:

// Update the relevant fields with the new data.
const setDOMInfo = info => {
  document.getElementById('total').textContent = info.total;
  document.getElementById('inputs').textContent = info.inputs;
  document.getElementById('buttons').textContent = info.buttons;
};

// Once the DOM is ready...
window.addEventListener('DOMContentLoaded', () => {
  // ...query for the active tab...
  chrome.tabs.query({
    active: true,
    currentWindow: true
  }, tabs => {
    // ...and send a request for the DOM info...
    chrome.tabs.sendMessage(
        tabs[0].id,
        {from: 'popup', subject: 'DOMInfo'},
        // ...also specifying a callback to be called 
        //    from the receiving end (content script).
        setDOMInfo);
  });
});

popup.html:

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="popup.js"></script>
  </head>
  <body>
    <h3 style="font-weight:bold; text-align:center;">DOM Info</h3>
    <table border="1" cellpadding="3" style="border-collapse:collapse;">
      <tr>
        <td nowrap>Total number of elements:</td>
        <td align="right"><span id="total">N/A</span></td>
      </tr>
      <tr>
        <td nowrap>Number of input elements:</td>
        <td align="right"><span id="inputs">N/A</span></td>
      </tr>
      <tr>
        <td nowrap>Number of button elements:</td>
        <td align="right"><span id="buttons">N/A</span></td>
      </tr>
    </table>
  </body>
</html>
gkalpak
fonte
Uau ! obrigado irmão, verificando sua abordagem agora sinto quantos problemas criei com meu código. Muito obrigado, isso funciona como um encanto. :)
Sumair Baloch
olá, desculpe, eu não vou há algum tempo, apenas verifiquei seu comentário. Não posso aceitar nenhuma resposta. diz que você precisa de 15 pontos de reputação para fazer isso.
Sumair Baloch
Você se importaria de me ajudar a resolver um problema muito semelhante? <a href=" stackoverflow.com/questions/34467627/…> Minha chance atual é basicamente o seu código a partir desta resposta. Mas ainda não consigo fazê-lo funcionar e não consigo encontrar uma boa ajuda sobre o assunto.
wuno
porque você usou chrome.tabs.sendMessageem popupjs e chrome.runtime.onMessage.addListenerem content.js porque .tabsem pop-upjs e .runtimeem content.js
Habib Kazemi
2
@hkm: Porque é assim que você envia e recebe mensagens. Está tudo nos documentos .
gkalpak
7

Você pode usar localStorage para isso. Você pode armazenar quaisquer dados em formato de tabela hash na memória do navegador e acessá-los a qualquer momento. Não tenho certeza se podemos acessar localStorage a partir do script de conteúdo (ele foi bloqueado antes), tente fazer isso sozinho. Veja como fazer isso por meio de sua página de plano de fundo (eu passo os dados do script de conteúdo para a página de plano de fundo primeiro e, em seguida, salvo em localStorage):

em contentScript.js:

chrome.runtime.sendMessage({
  total_elements: totalElements // or whatever you want to send
});

em eventPage.js (sua página de plano de fundo):

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse){
       localStorage["total_elements"] = request.total_elements;
    }
);

Então você pode acessar essa variável em popup.js com localStorage ["total_elements"].

Talvez você possa acessar localStorage diretamente do script de conteúdo em navegadores modernos. Então você não precisa passar os dados pela página de fundo.

Boa leitura sobre localStorage: http://diveintohtml5.info/storage.html

gthacoder
fonte
1
Por favor, não promova o uso do obsoleto chrome.extension.onRequest / sendRequest (que btw não carrega uma página de fundo não persistente antes de executar). Use chrome.runtime. * Em vez disso.
gkalpak
1
@ExpertSystem Por favor, se você vir essas informações desatualizadas, sugira / faça uma edição.
Xan
3
@Xan: Então, você é o sucessor da [extensão do Google Chrome] :) Não gosto de editar as postagens das pessoas (acho isso muito intrusivo). Além disso, com tantos tutoriais e exemplos desatualizados (pelo menos naquela época), achei melhor ter um comentário explícito sobre por que é ruim de usar .extension.xxx, em vez de apenas mostrar o caminho certo (que algumas pessoas podem pensar como " alternativa igualmente OK "). É mais uma questão de estilo, eu acho. Mantenha o bom trabalho !
gkalpak