Extension Chrome comment envoyer des données du script de contenu vers popup.html

96

Je sais que cela a été demandé dans de nombreux messages, mais honnêtement, je ne les comprends pas. Je suis nouveau dans JavaScript, les extensions Chrome et tout et j'ai ce devoir de classe. J'ai donc besoin de créer un plugin qui compterait les objets DOM sur n'importe quelle page donnée en utilisant les requêtes de domaine croisé. Jusqu'à présent, j'ai pu atteindre cet objectif en utilisant les API d'extension Chrome. Maintenant, le problème est que je dois afficher les données sur ma page popup.html à partir du fichier contentScript.js. Je ne sais pas comment faire, j'ai essayé de lire la documentation, mais la messagerie dans Chrome, je ne comprends tout simplement pas quoi faire.

Voici le code jusqu'à présent.

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 

});

Toute aide serait appréciée et s'il vous plaît éviter toute noobness que j'ai faite :)

Sumair Baloch
la source

Réponses:

172

Bien que vous soyez définitivement dans la bonne direction (et en fait assez proche de la fin), il y a plusieurs (imo) mauvaises pratiques dans votre code (par exemple, injecter une bibliothèque entière (jquery) pour une tâche aussi triviale, déclarer des autorisations inutiles, rendre superflus appels aux méthodes API, etc.).
Je n'ai pas testé votre code moi-même, mais après un bref aperçu, je pense que la correction de ce qui suit pourrait aboutir à une solution de travail (bien que pas très proche de l'optimum):

  1. Dans manifest.json : modifiez l'ordre des scripts de contenu, en mettant jquery en premier. Selon les documents pertinents :

    "js" [...] La liste des fichiers JavaScript à injecter dans les pages correspondantes. Ceux-ci sont injectés dans l'ordre dans lequel ils apparaissent dans ce tableau.

    (c'est moi qui souligne)

  2. En contentscript.js : Déplacer le chrome.runtime.sendMessage ({...}) bloc dans le onMessagerappel de l' écouteur.


Cela dit, voici ma proposition d'approche:

Flux de contrôle:

  1. Un script de contenu est injecté dans chaque page correspondant à certains critères.
  2. Une fois injectés, les scripts de contenu envoient un message à la page d'événement (aka page d'arrière-plan non persistante) et la page d'événement attache une action de page à l'onglet.
  3. Dès que la fenêtre contextuelle d'action de page est chargée, elle envoie un message au script de contenu, lui demandant les informations dont il a besoin.
  4. Le script de contenu traite la demande et répond afin que la fenêtre contextuelle d'action de page puisse afficher les informations.

Structure du répertoire:

          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
la source
Hou la la ! merci mon frère, en vérifiant votre approche maintenant je sens combien de problèmes j'ai créés avec mon code. Merci beaucoup, cela fonctionne comme un charme. :)
Sumair Baloch
bonjour, Désolé, je n'ai pas été sur depuis un certain temps, je viens de vérifier votre commentaire. Je ne peux accepter aucune réponse. il dit que vous avez besoin de 15 points de réputation pour le faire.
Sumair Baloch
Pourriez-vous m'aider à résoudre un problème très similaire? <a href=" stackoverflow.com/questions/34467627/…> Mon point de vue actuel est à peu près le code de cette réponse. Mais je ne parviens toujours pas à le faire fonctionner et je ne parviens pas à trouver une bonne aide à ce sujet.
wuno
pourquoi vous avez utilisé chrome.tabs.sendMessagedans popupjs et chrome.runtime.onMessage.addListenerdans content.js pourquoi .tabspour popupjs et .runtimedans content.js
Habib Kazemi
2
@hkm: Parce que c'est ainsi que vous envoyez et recevez des messages. Tout est dans la documentation .
gkalpak
7

Vous pouvez utiliser localStorage pour cela. Vous pouvez stocker toutes les données dans un format de table de hachage dans la mémoire du navigateur et y accéder à tout moment. Je ne suis pas sûr que nous puissions accéder à localStorage à partir du script de contenu (il était auparavant bloqué), essayez de le faire vous-même. Voici comment le faire via votre page d'arrière-plan (je passe d'abord les données du script de contenu à la page d'arrière-plan, puis je les enregistre dans localStorage):

dans contentScript.js:

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

dans eventPage.js (votre page de fond):

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

Ensuite, vous pouvez accéder à cette variable dans popup.js avec localStorage ["total_elements"].

Peut-être que vous pouvez accéder à localStorage directement à partir du script de contenu dans les navigateurs modernes. Ensuite, vous n'avez pas besoin de transmettre les données via votre page d'arrière-plan.

Bonne lecture sur localStorage: http://diveintohtml5.info/storage.html

gthacoder
la source
1
Merci de ne pas promouvoir l'utilisation de la version obsolète chrome.extension.onRequest / sendRequest (qui btw ne charge pas une page d'arrière-plan non persistante avant de l'exécuter). Utilisez plutôt chrome.runtime. * .
gkalpak
1
@ExpertSystem S'il vous plaît, si vous voyez de telles informations obsolètes, suggérez / faites une modification.
Xan
3
@Xan: Donc, vous êtes le successeur de [Google-Chrome-Extension] :) Je n'aime pas éditer les messages des gens (je trouve ça trop intrusif). En outre, avec tant de tutoriels et d'exemples obsolètes (du moins à l'époque), j'ai trouvé préférable d'avoir un commentaire explicite sur les raisons pour lesquelles il est mauvais à utiliser .extension.xxx, au lieu de simplement montrer la bonne manière (ce que certaines personnes pourraient considérer comme un " alternative tout aussi OK "). C'est plus une question de style, je suppose. Continuez votre bon travail!
gkalpak