Je me heurte à un problème en envoyant des données à partir de mon script de fond vers le script de ma pageAction
. Mon script de contenu ajoute un <iframe />
et le JavaScript dans <iframe />
reçoit les données de mon script de fond, mais il ne semble pas être récupéré dans mon pageAction
.
Dans mon script de fond, j'ai quelque chose comme:
chrome.tabs.sendMessage(senderTab.tab.id, { foo:bar });
Où senderTab.tab.id
est le "expéditeur" dans " onMessage
Listener" dans mon script de fond.
Dans le JavaScript chargé par le <iframe />
injecté par mon script de contenu, j'ai quelque chose comme:
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) { console.log("received in iframe:", request); } });
Le <iframe />
reçoit le message exactement comme prévu.
J'ai mis le même JavaScript dans ma page_action.js
, mais il ne reçoit aucune donnée du script de fond. La pageAction est activée avec chrome.pageAction.show(senderTab.tab.id);
Avant d'appeler chrome.tabs.sendMessage(senderTab.tab.id ...
La page HTML est-elle jointe à ma page? Action ne fait-elle pas partie du même onglet? Puisque ce tabId
m'a permis d'activer / "afficher" l'icône, je pense que l'auditeur dans le JavaScript pour la pageAction devrait également recevoir de chrome.tabs.sendMessage(senderTab.tab.id ...
Dans mon script de contenu, j'utilise les éléments suivants pour envoyer des données au script de fond:
chrome.runtime.sendMessage({ foo: bar });
Lorsque le script de contenu envoie le message ci-dessus, le JavaScript de pageAction le ramène.
Comment puis-je obtenir le script en arrière-plan pour envoyer correctement les données à ma pageAction? Je ne souhaite pas avoir la demande / le sondage de pageAction, mais je souhaite que la pageAction écoute et reçoive. Par exemple, si le HTML de la page s'affiche, il devrait pouvoir être mis à jour en temps réel lorsque la page d'arrière-plan fait des modifications.
Les pages ouvertes dans le contexte contextuel incluent:
background
pages d' background
manifest.json restent chargées en tout temps .) L'utilisation de tabs.sendMessage()
( MDN ) n'envoie aucun message à l'un d'entre eux. Vous runtime.sendMessage()
utiliser runtime.sendMessage()
( MDN ) pour lui envoyer un message. La portée de chacun d'eux, sauf les pages d'arrière-plan et les pages d'événements, n'existe que lorsqu'il est affiché. Évidemment, vous ne pouvez pas communiquer avec le code s'il n'existe pas. Lorsque la portée existe, vous pouvez communiquer avec l'une d'entre elles en utilisant:
Directement
À partir du contexte de fond, vous pouvez directement modifier des variables ou des fonctions d'appel, dans une autre page qui est également dans le contexte arrière (c.-à-d. Pas les scripts de contenu), après avoir obtenu une référence à sa portée globale, sa fenêtre , en utilisant extension.getViews()
( MDN ) , extension.getBackgroundPage()
( MDN ) ou autre méthode ( MDN ) .
Par exemple, vous pouvez appeler une fonction créée avec la function myFunction
dans la page de la première vue retournée en utilisant quelque chose comme:
winViews = chrome.extension.getViews(); winViews[0].myFunction(foo);
Il convient de noter que dans votre rappel de tabs.create()
( MDN ) ou windows.create()
( MDN ), la vue pour l'onglet nouvellement ouvert ou la fenêtre n'aura probablement pas encore existé. Vous devrez utiliser une méthodologie pour attendre l'existence de cette vue. 2 Voir ci-dessous les moyens recommandés de communiquer avec les onglets nouvellement ouverts ou Windows.
La manipulation directe des valeurs dans la portée de l'autre page vous permet de communiquer tout type de données que vous désirez.
Messagerie
Recevez des messages à l'aide de chrome.runtime.onMessage
( MDN ) , 3 qui ont été envoyés avec chrome.runtime.sendMessage()
( MDN ) . Chaque fois que vous recevez un message en cours d' runtime.onMessage
écoute de runtime.onMessage
, il y aura une fonction sendResponse
fournie en tant que troisième argument qui vous permet de répondre directement au message. Si l'expéditeur original n'a pas fourni de rappel pour recevoir une telle réponse dans son appel à chrome.runtime.sendMessage()
, la réponse est perdue. Si vous utilisez Promises (par exemple, browser.runtime.sendMessage()
dans Firefox), la réponse est passée comme un argument lorsque la promesse est remplie. Si vous souhaitez envoyer la réponse de manière asynchrone, vous devrez return true;
De votre runtime.onMessage
auditeur de runtime.onMessage
.
Ports
Vous pouvez également connecter des ports, en utilisant chrome.runtime.connect()
( MDN ) et chrome.runtime.onConnect
( MDN ) pour une messagerie à plus long terme.
Utilisez chrome.tabs.sendMessage()
pour envoyer des scripts de contenu
Si vous souhaitez envoyer du contexte d'arrière-plan (p. Ex. Script en arrière plan ou popup) à un script de contenu, vous utiliserez chrome.tabs.sendMessage()
/ chrome.runtime.onMessage
ou connectez le (s) port (s) à l'aide de chrome.tabs.connect()
( MDN ) / chrome.runtime.onConnect
.
Données sérialisables JSON uniquement
À l'aide de la messagerie, vous ne pouvez passer que des données qui sont sérialisées par JSON.
Les messages sont reçus par tous les scripts en arrière-plan, à l'exception de l'expéditeur
Les messages envoyés au contexte d'arrière-plan sont reçus par tous les scripts dans le contexte d'arrière-plan qui ont enregistré un auditeur, à l'exception du script qui l'a envoyé. 3 Il n'y a aucun moyen de préciser qu'il ne doit être reçu que par un script spécifique. Ainsi, si vous avez plusieurs destinataires potentiels, vous devrez créer un moyen de s'assurer que le message reçu était destiné à ce script. Les façons de le faire dépendent généralement de propriétés spécifiques existantes dans le message (par exemple, utiliser une destination
ou une propriété de recipient
pour indiquer quel script est de le recevoir ou définir que certains type
de messages sont toujours pour un destinataire ou un autre) ou pour différencier En fonction de l' sender
( MDN ) fourni au gestionnaire de messages (par exemple, si les messages d'un expéditeur ne sont toujours que pour un destinataire spécifique). Il n'y a aucun moyen défini de le faire, vous devez choisir / créer un moyen de le faire pour l'utiliser dans votre extension.
Pour une discussion plus détaillée de ce problème, veuillez consulter: Les messages destinés à un script dans le contexte d'arrière-plan sont reçus par tous
Données dans un StorageArea
Enregistrez les données dans un StorageArea ( MDN ) et soyez informé de la modification d'autres scripts à l'aide de chrome.storage.onChanged
( MDN ) . L'événement storage.onChanged
peut être écouté à la fois dans le contexte d'arrière-plan et les scripts de contenu.
Vous ne pouvez stocker que des données qui sont JSON-serializable dans un StorageArea.
La méthode la mieux utilisée dans une situation particulière dépend de ce que vous désirez communiquer (type de données, changement d'état, etc.) et à quelle partie ou partie de votre extension vous souhaitez communiquer à partir de . Par exemple, si vous souhaitez communiquer des informations qui ne sont pas sérialisables par JSON, vous devrez le faire directement (c'est-à-dire pas de messagerie ou d'utiliser un StorageArea). Vous pouvez utiliser plusieurs méthodes dans la même extension.
Aucune des fenêtres contextuelles (p. Ex. Action du navigateur ou action de la page) n'est directement associée à l'onglet actif. Il n'y a pas de concept d'instance partagée ou séparée par onglet. Cependant, l'utilisateur peut ouvrir une fenêtre contextuelle dans chaque fenêtre de Chrome. Si plus d'une fenêtre contextuelle est ouverte (un maximum d'une par fenêtre Chrome), chacune est dans une instance distincte (champ séparé, possède sa propre fenêtre) mais se trouve dans le même contexte. Lorsqu'une popup est réellement visible, elle existe dans le contexte d'arrière-plan.
Il n'y a qu'une seule action de la page ou une action du navigateur ouverte à la fois par fenêtre Chrome. Le fichier HTML qui sera ouvert sera celui qui a été défini pour l'onglet actif de la fenêtre actuelle et ouvert par l'utilisateur en cliquant sur le bouton d'action page / navigateur . On peut attribuer un document HTML différent pour différents onglets en utilisant chrome.browserAction.setPopup()
( MDN ) ou chrome.pageAction.setPopup()
( MDN ) et en spécifiant un tabId
. Le popup peut / sera détruit pour plusieurs raisons, mais certainement quand un autre onglet devient l'onglet actif dans la fenêtre dans laquelle la fenêtre contextuelle est ouverte.
Cependant, toute méthode de communication utilisée ne communiquera que à celle (s) qui est / sont actuellement ouverte, pas celles qui ne sont pas ouvertes. Si les fenêtres pop-up sont ouvertes pour plus d'une fenêtre Chrome à la fois, elles sont des instances distinctes, avec leur propre champ d'application (c'est-à-dire leur propre fenêtre). Vous pouvez penser à cela comme si la même page Web était ouverte dans plus d'un onglet.
Si vous avez un script en arrière-plan, le contexte du script d'arrière-plan est persistant dans toute l'instance de Chrome. Si vous n'avez pas de script en arrière-plan, le contexte peut être créé au besoin (par exemple, une fenêtre contextuelle s'affiche) et détruit lorsqu'il n'est plus nécessaire.
chrome.tabs.sendMessage()
ne peut pas communiquer avec les fenêtres pop-up Comme mentionné ci-dessus, même si le popup existe, il existera dans le contexte d'arrière-plan. Appeler chrome.tabs.sendMessage()
envoie un message aux scripts de contenu injectés dans un onglet / cadre , pas dans le contexte de fond. Ainsi, il n'émettra pas un message à un script sans contenu comme une fenêtre contextuelle.
En appelant chrome.pageAction.show()
( MDN ), il suffit que le bouton d' action de la page s'affiche. Il ne provoque pas d' affichage contextuel associé. Si la page popup / options / autre page n'est pas affichée (pas seulement le bouton), sa portée n'existe pas. Quand il n'existe pas, il est évident qu'il ne peut pas recevoir de message
Au lieu de la capacité de l'action de la page de show()
( MDN ) ou de hide()
( MDN ), le bouton, les actions du navigateur peuvent enable()
( MDN ) ou disable()
( MDN ) le bouton.
Vous pouvez utiliser tabs.create()
( MDN ) ou windows.create()
( MDN ) pour ouvrir un onglet ou une fenêtre contenant une page HTML à partir de votre extension. Cependant, le rappel pour ces deux appels API est exécuté avant le DOM de la page existant et donc avant tout JavaScript associé à la page existante. Ainsi, vous ne pouvez pas accéder immédiatement au DOM créé par le contenu de cette page, ni interagir avec le JavaScript pour la page. Très précisément: pas de runtime.onMessage()
auditeurs auront été ajoutés, de sorte qu'aucun message envoyé à ce moment ne sera reçu par la nouvelle page d'ouverture.
Les meilleurs moyens de résoudre ce problème sont les suivants:
chrome.extension.getBackgroundPage()
pour lire les données directement. storage.local
( MDN ) . La page d'ouverture peut alors le lire lorsque son JavaScript est exécuté. Par exemple, vous pouvez utiliser une touche appelée messageToNewExtensionPage
. runtime.sendMessage()
, runtime.sendMessage()
le transfert des données à partir de votre nouvelle page d'ouverture en envoyant un message du code de cette page à la source des données (en utilisant runtime.sendMessage()
ou tabs.sendMessage()
Pour les sources de script de contenu) demandant les données. Le script avec les données peut alors envoyer les données à l'aide de la fonction sendResponse
(MDN) fournie par runtime.onMessage()
. Il existe plusieurs méthodes que vous pouvez utiliser. La meilleure façon dépendra exactement de ce que vous faites (par exemple, lorsque vous devez accéder à la vue en ce qui concerne le code exécuté dans la vue). Une méthode simple serait juste de faire un sondage en attendant l'existence de la vue. Le code suivant fait cela pour ouvrir une fenêtre:
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. }
Dans les versions de Firefox avant la version 51, l'écouteur d'exécution sera appelé pour les messages envoyés à partir du même script (par exemple, les messages envoyés par le script d'arrière-plan seront également reçus par le script en arrière-plan). Dans ces versions de Firefox, si vous appelez inconditionnellement runtime.sendMessage () à partir d'un runtime.onMessage-écouteur, vous configurerez une boucle infinie qui allongera la CPU et verrouillera Firefox. Si vous devez appeler runtime.sendMessage () à partir d'un runtime.onMessage, vous devrez vérifier la propriété sender.url pour vérifier que vous n'émetz pas de message en réponse à un message qui a été envoyé à partir du même script. Ce bug a été résolu à partir de Firefox 51.