2015-06-29 17 views
6

Sto scherzando (cercando di imparare) come creare un'estensione di Chrome. In questo momento sto facendo una cosa super semplice dove conta istanze di una certa parola su una pagina. Ho questa parte di lavoro.Passa una variabile dallo script di contenuto al popup

Quello che voglio fare è inviare questa informazione al pop così posso usarla per fare altre cose.

Ecco quello che ho finora:

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(); 

Quindi quello che voglio che accada è quello di inviare la variabile count al popup in modo che io possa usarlo lì.

Mi sono guardato intorno e ho scoperto che ho bisogno di fare qualcosa con chrome.runtime.sendMessage.

ce l'ho così ho aggiungere questa linea alla fine del content.js:

chrome.runtime.sendMessage(count); 

e poi in background.js:

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

Sono una specie di bloccato qui come ho Non sono sicuro di come inviare queste informazioni a popup e usarle.

risposta

25

Come hai notato correttamente, non è possibile inviare i dati direttamente al popup quando è chiuso. Quindi, stai inviando dati alla pagina di sfondo.

Quindi, quando si apre il popup, si desidera che i dati lì. Quindi, quali sono le opzioni?

Nota: questa risposta darà prima un cattivo consiglio e quindi migliorerà. Dal momento che OP sta imparando, è importante mostrare il processo di pensiero e il roadbumps.


La prima soluzione che mi viene in mente è la seguente: richiedi la pagina di sfondo, utilizzando nuovamente Messaggi. Avviso precoce: questo non funzionerà o funzionerà male

Prima di tutto, stabilire che ci possono essere diversi tipi di messaggi. Modificare il vostro codice di messaggistica corrente:

// 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); 
     } 
    } 
); 

E ora, si potrebbe in teoria chiedere che nel popup:

// 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); 
     } 
    } 
); 

Ora, quali sono i problemi con questo?

  1. Qual è la durata di temp? Hai esplicitamente dichiarato "persistent": false in your manifest. Di conseguenza, la pagina di sfondo può essere scaricata in qualsiasi momento, cancellando lo stato come temp.

    È possibile risolvere il problema con "persistent": true, ma continua a leggere.

  2. Quale conto di tabulazione ci si aspetta di vedere? temp avrà gli ultimi dati scritti, che potrebbero non essere la scheda corrente.

    È possibile risolvere il problema mantenendo le schede (vedere cosa ho fatto lì?) su quale scheda inviato i dati, ad es. utilizzando:

    // 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) { 
         /* ... */ 
        }); 
    }); 
    

    Tuttavia, è un sacco di lavoro, non è vero? Questa soluzione funziona bene anche se per i dati specifici non-tab, dopo aver fissato 1.


miglioramento successivo di prendere in considerazione: abbiamo bisogno lo sfondo della pagina per memorizzare il risultato per noi? Dopo tutto, chrome.storage is a thing; è una memoria persistente a cui possono accedere tutti gli script di estensione (inclusi gli script di contenuto).

Questo riduce lo sfondo (e Messaging) fuori dal quadro:

// 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 
    } 
}); 

Questo sembra più pulito, e ignora completamente il problema 1 dall'alto, ma il problema 2 ottiene più complicato. You can't directly set/read qualcosa come count[id] nello spazio di archiviazione, è necessario leggere count, modificarlo e riscriverlo. Può diventare lento e disordinato.

Aggiungete a ciò che gli script di contenuto non sono realmente a conoscenza del loro ID di tabulazione; dovrai imparare lo sfondo del messaggio solo per impararlo. Ugh. Non carino. Ancora una volta, questa è un'ottima soluzione per dati non specifici della scheda.


Allora la prossima domanda da porsi: perché abbiamo bisogno anche di una posizione centrale per memorizzare il risultato (specifico per scheda)? La durata del copione del contenuto è la durata della pagina. Puoi chiedere lo script del contenuto direttamente in qualsiasi momento. Compreso dal popup.

Aspetta, aspetta, non hai detto in alto non è possibile inviare i dati al popup? Beh, sì, kinda: quando non sai se è lì ad ascoltare. Ma se il popup chiede, allora deve essere pronto per ottenere una risposta, no?

Quindi, invertiamo la logica dello script del contenuto. Invece di inviare immediatamente i dati, aspettare e ascoltare le richieste:

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

Poi, nel popup, abbiamo bisogno di interrogare la schedache contiene lo script contenuti. È una funzione di messaggistica diversa e dobbiamo specificare l'ID della scheda.

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

Ora è molto più pulito. Il problema 2 è risolto: chiediamo la scheda da cui vogliamo ascoltare. Il problema 1 sembra essere risolto: purché uno script contenga ciò di cui abbiamo bisogno, può rispondere.

Si noti, come ultima complicazione, che gli script di contenuto non vengono sempre iniettati quando ci si aspetta che: inizino solo ad attivarsi nella navigazione dopo l' l'estensione è stata (ri) caricata. Ecco lo an answer che lo spiega dettagliatamente. Può essere aggirato se lo si desidera, ma per ora solo un percorso di codice per esso:

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 
    } 
} 
+0

molto approfondita come al solito – Auspex

+0

Grazie @Xan per la risposta dettagliata. Solo i problemi che non riesco a risolvere con l'approccio 3 è - se invio un messaggio basato su tab da popup.js e lascia che lo script di contenuto restituisca i dati, cosa succede se l'utente passa a una scheda diversa e la scheda di script del contenuto non è più il scheda attiva? Il mio pop-up non mostrerà mai risultati? –

+0

Se l'utente si sposta in una scheda diversa, il popup viene chiuso nel processo e non c'è nessun listener in ogni caso. Questo metodo è per qualcosa a cui lo script di contenuto può rispondere immediatamente. Se pensi che il popup possa chiudersi nel mezzo, devi lasciare che la tua pagina di sfondo parli e poi il popup estrae i dati da esso. – Xan

Problemi correlati