Browser-Addons: Nachrichten-Broadcast an alle Tabs
Situation
Meine Extension injiziert auf bestimmten Seiten ein Content Script. Man kann dann auf dieser veränderten Seite auch Einstellungen ändern, die im der Extension zugewiesenen Storage gespeichert wird. Dafür wird wie üblich eine Nachricht mit den Änderungen an den Hintergrundsprozess geschickt.
Problem
Angenommen, ich habe zwei oder mehr Tabs offen. Nun verändere ich die Einstellung A in einem dieser Tabs. Die Änderung wird gespeichert, aber alle anderen Tabs zeigen nach wie vor den alten Zustand an. Erst nach einem Neuladen der Seite wird die Änderung sichtbar.
Was wir wollen, ist eine Synchronisation der Tabs, was bedeutet, wir müssen alle im Hintergrund eintreffenden Änderungen allen anderen Tabs ebenfalls mitteilen. Und das geht so …
Lösung: Opera
opera.extension.broadcastMessage( msg );
Eeyup, das war es.
Lösung: Chrome
In Chrome müssen wir uns die IDs der Tabs merken, die unser Content Script beinhalten. Dafür sendet besagtes Content Script zuallererst immer eine Nachricht an den Hintergrund, damit wir uns seine Tab-ID merken können. Wird der Tab geschlossen, verwerfen wir die Tab-ID wieder.
var openTabs = []; chrome.extension.onMessage.addListener( handler ); function handler( e, sender, sendResponse ) { if( e.data.msg == "content script loaded" ) { openTabs.push( sender.tab.id ); chrome.tabs.onRemoved.addListener( onTabRemove ); } ... }; function onTabRemove( tabId, info ) { var idx = openTabs.indexOf( tabId ); if( idx >= 0 ) { openTabs.splice( idx, 1 ); } };
Ein Broadcast an alle relevanten Tabs sieht dann wie folgt aus.
for( var i = 0; i < openTabs.length; i++ ) { chrome.tabs.sendMessage( openTabs[i], msg, myCallbackFunction ); }
Lösung: Firefox
In Firefox gehen wir ähnlich vor wie in Chrome und merken uns alle Tabs, die unser Content Script inne haben, in der Variable workers
.
var self = require( "self" ); var pageMod = require( "page-mod" ); var workers = []; pageMod.PageMod( { include: "*.mozilla.org", attachTo: ["existing", "top"], contentScriptWhen: "ready", contentScriptFile: [self.data.url( "myContentScript.js" )], onAttach: handleOnAttach } ); function handleOnAttach( worker ) { workers.push( worker ); worker.on( "detach", function() { forgetWorker( this ); } ); }; function forgetWorker( worker ) { var idx = workers.indexOf( worker ); if( idx >= 0 ) { workers.splice( idx, 1 ); } };
Unser Broadcast funktioniert dann wie folgt.
for( var i = 0; i < workers.length; i++ ) { workers[i].postMessage( msg ); }