Comunica tra gli script nel contesto di sfondo (script in background, azione del browser, azione della pagina, pagina delle opzioni, ecc.)

Ho pageAction un problema nell’invio dei dati dal mio script in background allo script per la mia pageAction . Il mio script di contenuto aggiunge un e il JavaScript in sta ricevendo i dati dal mio script in background, ma non sembra essere stato recuperato nella mia pageAction .

Nel mio background script ho qualcosa del tipo:

 chrome.tabs.sendMessage(senderTab.tab.id, { foo:bar }); 

dove senderTab.tab.id è il “mittente” nel listener di onMessage nel mio script in background.

Nel JavaScript caricato da inserito dal mio script di contenuto ho qualcosa del tipo:

 chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) { console.log("received in iframe:", request); } }); 

riceve il messaggio esattamente come previsto.

page_action.js lo stesso JavaScript nel mio page_action.js , ma non riceve alcun dato dallo script in background. La paginaAzione è triggersta con chrome.pageAction.show(senderTab.tab.id); prima chiamo chrome.tabs.sendMessage(senderTab.tab.id ...

La pagina HTML è collegata alla mia pagina? Azione che non fa parte della stessa scheda? Dato che questo tabId mi ha permesso di triggersre / “mostrare” l’icona, penserei che il listener nel JavaScript per la paginaAction dovrebbe ricevere anche da chrome.tabs.sendMessage(senderTab.tab.id ...


Nel mio script di contenuto utilizzo quanto segue per inviare dati allo script di background:

 chrome.runtime.sendMessage({ foo: bar }); 

Quando lo script di contenuto invia il messaggio precedente, la pagina Action JavaScript lo sta rilevando.


Come posso ottenere lo script in background per inviare correttamente i dati alla mia paginaAction? Non desidero avere una richiesta / sondaggio di richiesta di pagine, ma voglio che l’azione di pagina sia solo in ascolto e in ricezione. Ad esempio, se la pagina Action HTML mostrata, dovrebbe essere in grado di aggiornare in tempo reale come la pagina di sfondo apporta modifiche.

Comunicazione con una pagina nel contesto di sfondo

Le pagine che sono aperte nel contesto di sfondo includono:

  • pagine di sfondo / script ( MDN )
  • pagine di eventi (Firefox non supporta pagine di eventi. Tutte le pagine di background manifest.json rimangono sempre caricate ).
  • browser action popups ( MDN )
  • page action popups ( MDN )
  • pagine di opzioni ( MDN1 , MDN2 ) (in un popup, una scheda o una finestra)
  • pagine azione della barra laterale (NDN) (non disponibile in Chrome)
  • Qualsiasi contenuto HTML contenuto all’interno dell’estensione che viene aperto normalmente in una scheda, in una finestra (ad esempio un pannello) o in una cornice. 1

L’utilizzo di tabs.sendMessage() ( MDN ) non invierà un messaggio a nessuno di essi. Dovresti utilizzare runtime.sendMessage() ( MDN ) per inviare un messaggio a loro. L’ambito di ognuno di essi, ad eccezione delle pagine di sfondo e delle pagine degli eventi, esiste solo quando viene visualizzato. Ovviamente, non puoi comunicare con il codice quando non esiste. Quando esiste l’ambito, puoi comunicare con qualcuno di loro usando:

  • Direttamente
    Dal contesto di sfondo, è ansible modificare direttamente le variabili, o richiamare funzioni, in un’altra pagina che si trova anche nel contesto di sfondo (cioè non script di contenuto), dopo aver ottenuto un riferimento al suo ambito globale, la sua finestra , usando extension.getViews() ( MDN ) , extension.getBackgroundPage() ( MDN ) o altro metodo ( MDN ) .
    Ad esempio, puoi chiamare una funzione creata con function myFunction nella pagina della prima vista restituita usando qualcosa come:

     winViews = chrome.extension.getViews(); winViews[0].myFunction(foo); 

    Va notato che nella richiamata da tabs.create() ( MDN ) o windows.create() ( MDN ) probabilmente la vista per la scheda o finestra appena aperta non esiste ancora. Dovrai utilizzare una metodologia per attendere che la vista esista. 2 Vedi sotto per i modi consigliati per comunicare con tabs o windows appena aperte.

    La manipolazione diretta dei valori nell’ambito dell’altra pagina consente di comunicare qualsiasi tipo di dati desiderato.

  • messaggistica
    Ricevi messaggi usando chrome.runtime.onMessage ( MDN ) , 3 che sono stati inviati con chrome.runtime.sendMessage() ( MDN ) . Ogni volta che si riceve un messaggio in un listener runtime.onMessage , verrà sendResponse una funzione sendResponse come terzo argomento che consente di rispondere direttamente al messaggio. Se il mittente originale non ha fornito un callback per ricevere tale risposta nella loro chiamata a chrome.runtime.sendMessage() , la risposta viene persa. Se si utilizza Promises (ad es. browser.runtime.sendMessage() in Firefox), la risposta viene passata come argomento quando la Promessa è soddisfatta. Se si desidera inviare la risposta in modo asincrono, sarà necessario return true; dal tuo listener runtime.onMessage .

    Ports
    È anche ansible colbind le porte, utilizzando chrome.runtime.connect() ( MDN ) e chrome.runtime.onConnect ( MDN ) per la messaggistica a più lungo termine.

    Usa chrome.tabs.sendMessage() per inviare agli script di contenuto
    Se si desidera inviare dal contesto di sfondo (ad esempio script di background o popup) a uno script di contenuto, si utilizzerà chrome.tabs.sendMessage() / chrome.runtime.onMessage o si connettono port (s) utilizzando chrome.tabs.connect() ( MDN ) / chrome.runtime.onConnect .

    Solo dati serializzabili JSON
    Usando la messaggistica, puoi solo passare dati che sono serializzabili con JSON.

    I messaggi vengono ricevuti da tutti gli script in background, ad eccezione del mittente
    I messaggi inviati al contesto di sfondo vengono ricevuti da tutti gli script nel contesto di sfondo che hanno registrato un listener, ad eccezione dello script che lo ha inviato. 3 Non c’è modo di specificare che è solo per essere ricevuto da uno specifico script. Pertanto, se si dispone di più destinatari potenziali, sarà necessario creare un modo per assicurarsi che il messaggio ricevuto sia destinato a tale script. I modi per farlo di solito si basano su proprietà specifiche esistenti nel messaggio (ad esempio, utilizzare una destination o una proprietà del recipient per indicare quale script è per riceverlo o definire che alcuni type di messaggi siano sempre per un destinatario o un altro) o per differenziare basato sul sender ( MDN ) fornito al gestore del messaggio (ad esempio se i messaggi di un mittente sono sempre solo per un destinatario specifico). Non c’è un modo per farlo, devi scegliere / creare un modo per farlo nel tuo interno.

    Per una discussione più dettagliata di questo problema, vedere: I messaggi destinati a uno script nel contesto di sfondo vengono ricevuti da tutti

  • Dati in un’Archiviazione
    Memorizza i dati su StorageArea ( MDN ) e ricevi una notifica della modifica in altri script usando chrome.storage.onChanged ( MDN ) . L’evento storage.onChanged può essere ascoltato sia nel contesto di sfondo che negli script di contenuto.

    È ansible archiviare solo i dati che sono serializzabili da JSON in StorageArea.

Il metodo migliore da utilizzare in qualsiasi situazione particolare dipende da ciò che si desidera comunicare (tipo di dati, modifica dello stato, ecc.) E da quale parte o parti dell’estensione si desidera comunicare da e verso . Ad esempio, se si desidera comunicare informazioni che non sono serializzabili con JSON, è necessario farlo direttamente (ovvero non utilizzare messaggi o utilizzare StorageArea). Puoi utilizzare più metodi nella stessa estensione.

Altro su popup

Nessuno dei popup (ad esempio azione del browser o azione della pagina) è direttamente associato alla scheda triggers. Non esiste un concetto di istanza condivisa o separata per scheda. Tuttavia, l’utente può aprire un popup in ogni finestra di Chrome. Se è aperto più di un popup (massimo uno per finestra di Chrome), allora ognuno si trova in un’istanza separata (ambito separato, ha una propria finestra), ma si trova nello stesso contesto. Quando un popup è effettivamente visibile, esiste nel contesto di sfondo.

C’è sempre una sola azione di pagina o un popup di azione del browser aperto in un momento per finestra di Chrome. Il file HTML che verrà aperto sarà quello che è stato definito per la scheda triggers della finestra corrente e aperto dall’utente facendo clic sul pulsante di azione della pagina / browser . A questo può essere assegnato un documento HTML diverso per diverse tabs utilizzando chrome.browserAction.setPopup() ( MDN ) o chrome.pageAction.setPopup() ( MDN ) e specificando un tabId . Il popup può / viene distrutto per diversi motivi, ma sicuramente quando un’altra scheda diventa la scheda triggers nella finestra in cui è aperto il popup.

Tuttavia, qualsiasi metodo di comunicazione utilizzato comunicherà solo a quello che è / sono attualmente aperti, non quelli che non sono aperti. Se i popup sono aperti per più di una finestra di Chrome alla volta, sono istanze separate, con il loro ambito (cioè la propria finestra). Puoi pensare a qualcosa come avere la stessa pagina aperta in più di una scheda.

Se si dispone di uno script in background, il contesto dello script in background è persistente sull’intera istanza di Chrome. Se non si dispone di uno script in background, il contesto può essere creato quando necessario (ad esempio viene mostrato un popup) e distrutto quando non è più necessario.

chrome.tabs.sendMessage() non può comunicare ai popup

Come accennato in precedenza, anche se il popup esistesse, esisterà nel contesto di sfondo. La chiamata chrome.tabs.sendMessage() invia un messaggio agli script di contenuto immessi in una scheda / frame , non al contesto di sfondo. Pertanto, non invierà un messaggio a uno script non contenuto come un popup.

Pulsante di azione: abilita / disabilita (azione del browser) vs. mostra / nascondi (azione della pagina)

La chiamata chrome.pageAction.show() ( MDN ) fa sì che il pulsante di azione della pagina venga visualizzato. Non fa apparire alcun popup associato. Se la pagina popup / opzioni / altra pagina non viene effettivamente mostrata (non solo il pulsante), il suo ambito non esiste. Quando non esiste, ovviamente non può ricevere alcun messaggio

Invece dell’abilità di azione della pagina di show() ( MDN ) o hide() ( MDN ) il pulsante, le azioni del browser possono enable() ( MDN ) o disable() ( MDN ) il pulsante.

Apertura a livello di programmazione di una scheda o di una finestra con HTML dall’estensione

È ansible utilizzare tabs.create() ( MDN ) o windows.create() ( MDN ) per aprire una scheda o una finestra contenente una pagina HTML all’interno della propria estensione. Tuttavia, il callback per entrambe le chiamate API viene eseguito prima del DOM della pagina esistente e quindi precedente a qualsiasi JavaScript associato alla pagina esistente. Pertanto, non puoi accedere immediatamente al DOM creato dai contenuti di quella pagina, né interagire con il JavaScript per la pagina. In particolare: nessun runtime.onMessage() sarà stato aggiunto agli ascoltatori, quindi nessun messaggio inviato in quel momento verrà ricevuto dalla nuova pagina di apertura.

I modi migliori per risolvere questo problema sono:

  1. Avere i dati disponibili in modo che la nuova pagina di apertura possa ottenere i dati quando è pronta. Fai questo, prima di iniziare il processo di apertura della pagina:
    1. Se la sorgente si trova nel contesto di sfondo: archivia i dati in una variabile disponibile per l’ambito globale della pagina di invio. La pagina di apertura può quindi utilizzare chrome.extension.getBackgroundPage() per leggere direttamente i dati.
    2. Se la fonte dei dati è nel contesto di sfondo o in uno script di contenuto: posizionare i dati in storage.local ( MDN ) . La pagina di apertura può quindi leggerla quando viene eseguito il suo JavaScript. Ad esempio, è ansible utilizzare una chiave denominata messageToNewExtensionPage .
  2. Se si utilizza runtime.sendMessage() , avviare il trasferimento dei dati dalla nuova pagina di apertura inviando un messaggio dal codice di quella pagina all’origine dei dati (utilizzando runtime.sendMessage() o tabs.sendMessage() per le fonti di script del contenuto) che richiedono i dati. Lo script con i dati può quindi inviare nuovamente i dati utilizzando la funzione sendResponse (MDN) fornita da runtime.onMessage() .
  3. Aspettare di interagire con la nuova pagina di apertura fino a quando almeno il DOM è disponibile, se non fino a dopo il JavaScript per la pagina è stata eseguita. Anche se è ansible farlo senza la nuova pagina di apertura che fornisce una notifica specifica che è attivo e funzionante, farlo è più complesso e utile solo in alcuni casi specifici (ad esempio, si desidera fare qualcosa prima che venga eseguito JavaScript nella nuova pagina) . 2

Riferimenti aggiuntivi

Cromo

  • Messaggio in corso
  • Panoramica dell’estensione di Chrome
    • architettura
    • Comunicazione tra le pagine

Firefox

  • WebExtensions
  • Anatomia di una estensione del web

  1. Con alcune piccole eccezioni: ad esempio l’uso di uno script di contenuto per inserire il contenuto nel contesto della pagina.
  2. Esistono diversi metodi che è ansible utilizzare. Qual è il modo migliore per dipendere esattamente da quello che stai facendo (ad esempio quando devi accedere alla vista rispetto al codice che viene eseguito nella vista). Un metodo semplice sarebbe solo per il sondaggio in attesa che la vista esista. Il seguente codice lo fa per aprire una finestra:

     chrome.windows.create({url: myUrl},function(win){ //Poll for the view of the window ID. Poll every 50ms for a // maximum of 20 times (1 second). Then do a second set of polling to // accommodate slower machines. Testing on a single moderately fast machine // indicated the view was available after, at most, the second 50ms delay. waitForWindowId(win.id,50,20,actOnViewFound,do2ndWaitForWinId); }); function waitForWindowId(id,delay,maxTries,foundCallback,notFoundCallback) { if(maxTries--< =0){ if(typeof notFoundCallback === 'function'){ notFoundCallback(id,foundCallback); } return; } let views = chrome.extension.getViews({windowId:id}); if(views.length > 0){ if(typeof foundCallback === 'function'){ foundCallback(views[0]); } } else { setTimeout(waitForWindowId,delay,id,delay,maxTries,foundCallback ,notFoundCallback); } } function do2ndWaitForWinId(winId,foundCallback){ //Poll for the view of the window ID. Poll every 500ms for max 40 times (20s). waitForWindowId(winId,500,40,foundCallback,windowViewNotFound); } function windowViewNotFound(winId,foundCallback){ //Did not find the view for the window. Do what you want here. // Currently fail quietly. } function actOnViewFound(view){ //What you desire to happen with the view, when it exists. } 
  3. Da MDN :

    Nelle versioni di Firefox precedenti alla versione 51, il listener runtime.onMessage verrà chiamato per i messaggi inviati dallo stesso script (ad esempio, i messaggi inviati dallo script in background verranno anche ricevuti dallo script in background). In quelle versioni di Firefox, se si chiama incondizionatamente runtime.sendMessage () da un listener runtime.onMessage, verrà impostato un ciclo infinito che ridurrà al massimo la CPU e bloccherà Firefox. Se è necessario chiamare runtime.sendMessage () da un runtime.onMessage, sarà necessario controllare la proprietà sender.url per verificare che non si stia inviando un messaggio in risposta a un messaggio inviato dallo stesso script. Questo bug è stato risolto a partire da Firefox 51.