12 KiB
Swyx Tab Bridge — Firefox-Add-on
Verwaltet Firefox-Tabs über die WebSocket-Verbindung zu SwyxTray: Tabs auslesen, öffnen und schließen.
Rollenverteilung
Ein Browser-Add-on kann keinen WebSocket-Server betreiben — es kann nur Client sein.
Das Add-on verbindet sich daher zu SwyxTray (Default ws://127.0.0.1:17655) und
beantwortet die dort eintreffenden Aufträge.
[ SwyxTray, Port 17655 ] <--- verbindet sich --- [ Firefox-Add-on: WS-Client ]
| {"type":"tab","id":1,"action":"list"} ->
<- {"cmd":"tabresult","id":1,"ok":true,"tabs":[…]}
Zuständig ist das Add-on ausschließlich für Tab-Nachrichten ("type":"tab").
Alles andere auf der Verbindung — Telefonie-Kommandos, Events, Nachrichten an andere
Komponenten — wird ignoriert: keine Antwort, kein Fehler.
Protokoll
Alle Nachrichten sind JSON-Textframes. Die id der Anfrage wird unverändert in die
Antwort gespiegelt; die Antwort trägt immer "cmd":"tabresult".
1. Offene Tabs auslesen (list)
→ {"type":"tab","id":1,"action":"list"}
← {"cmd":"tabresult","id":1,"ok":true,
"tabs":[{"id":42,"title":"Kundenakte Muster GmbH","url":"https://crm.example.local/kunden/4711","active":false},
{"id":43,"title":"Posteingang","url":"https://mail.example.local/","active":true}]}
Optionale Felder zum Einschränken: currentWindowOnly (bool), windowId (number),
url / title (Match-Pattern, z.B. "*://*.example.local/*").
2. Tab mit mitgegebener URL öffnen (open)
→ {"type":"tab","id":2,"action":"open","url":"https://crm.example.local/kunden/4711"}
← {"cmd":"tabresult","id":2,"ok":true,"tabId":44}
Optionale Felder:
| Feld | Typ | Default | Beschreibung |
|---|---|---|---|
active |
boolean | true |
false = im Hintergrund öffnen |
reuse |
boolean | false |
ist die URL schon offen, wird der Tab aktiviert statt neu geöffnet (Antwort enthält dann "reused":true) |
newWindow |
boolean | false |
in neuem Fenster öffnen |
windowId |
number | – | Zielfenster |
index |
number | – | Position in der Tableiste |
pinned |
boolean | false |
angeheftet öffnen |
Erlaubt sind http, https, ftp, file; javascript:, data: u.ä. werden abgewiesen.
3. Tab löschen (close)
→ {"type":"tab","id":3,"action":"close","tabId":42}
← {"cmd":"tabresult","id":3,"ok":true}
tabId darf auch ein Array sein ([42,43]); die Antwort enthält dann "closed":[42,43].
Anmeldung (hello)
Direkt nach dem Verbindungsaufbau meldet sich das Add-on einmal an — immer, nicht nur mit Token:
← {"cmd":"hello","client":"swyx-tab-bridge","version":"1.0.0",
"actions":["list","open","close","activate","focus"]}
Ist unter Einstellungen ein Token gesetzt, kommt es als Feld token mit. Eine
Antwort wird nicht erwartet. SwyxTray kann die Gegenstelle erst dadurch
identifizieren und den Verbindungsaufbau protokollieren; ohne hello ist auf der
Leitung nicht unterscheidbar, wer sich verbunden hat.
Fehler
← {"cmd":"tabresult","id":5,"ok":false,"error":"Feld 'url' fehlt."}
error ist ein zusätzliches Klartextfeld zur Diagnose — maßgeblich ist "ok":false.
Fehlerfälle: fehlendes/ungültiges url bzw. tabId, unerlaubtes URL-Protokoll,
unbekannte action, nicht existierender Tab.
SwyxTray wertet einen Auftrag nach 5 s ohne Antwort als gescheitert. Das Add-on bricht
deshalb jede Aktion nach 4 s ab (ACTION_TIMEOUT_MS in background.js)
und antwortet dann mit "ok":false — es bleibt also nie stumm.
Zusätzliche Aktionen
Über die drei SwyxTray-Aktionen hinaus versteht das Add-on:
→ {"type":"tab","id":4,"action":"activate","tabId":42}
← {"cmd":"tabresult","id":4,"ok":true,"tabId":42}
→ {"type":"tab","id":5,"action":"focus","url":"https://crm.example.local/kunden/4711"}
← {"cmd":"tabresult","id":5,"ok":true,"focused":true,"opened":false,"tabId":42}
activateholt einen bekannten Tab pertabIdin den Vordergrund (inkl. Fenster).focussucht die passende Seite und aktiviert sie; ist sie nicht offen, wird sie geöffnet (focused:false, opened:true). Optional:match(prefix|exact|origin, Defaultprefix),open:false(nicht öffnen),activate:false, oder Suche pertitlestatturl. Fragment (#…) und abschließende Slashes werden beim Vergleich ignoriert.
Ignorierte Nachrichten
| Nachricht | Verhalten |
|---|---|
alles ohne "type":"tab" (z.B. {"cmd":"call",…}) |
wird ignoriert |
| ungültiges JSON | wird ignoriert |
Installation (temporär, zum Entwickeln)
about:debugging#/runtime/this-firefoxöffnen- „Temporäres Add-on laden…” →
manifest.jsonin diesem Ordner auswählen - Server-Adresse ggf. unter Einstellungen im Popup anpassen (Default
ws://127.0.0.1:17655)
Temporär geladene Add-ons verschwinden beim Firefox-Neustart. Für den Dauerbetrieb muss
das Add-on signiert werden (web-ext sign, AMO-Account) oder es wird Firefox ESR /
Developer Edition mit xpinstall.signatures.required = false verwendet.
Paket bauen:
npx web-ext lint
npx web-ext build # erzeugt web-ext-artifacts/*.zip -> signieren bzw. in .xpi umbenennen
Signierung und Datenerhebung
Seit dem 3. November 2025 müssen neue Erweiterungen auf AMO Mozillas eingebautes Einwilligungssystem bedienen. Im Manifest steht deshalb:
"data_collection_permissions": { "required": ["browsingActivity"] }
Mozilla definiert Übermittlung als Daten, die außerhalb des Add-ons oder des
lokalen Browsers verarbeitet werden. Das Add-on gibt Tab-Titel und URLs an
SwyxTray weiter — also aus dem Browser heraus, wenn auch nur an eine lokale
Anwendung auf demselben Rechner und nicht ins Netz. "none" wäre eine
Untertreibung, browsingActivity beschreibt genau das (besuchte Seiten, URLs).
strict_min_version bleibt bewusst bei 115.0, obwohl der Schlüssel erst ab
Firefox 140 ausgewertet wird. Ältere Versionen ignorieren ihn folgenlos; ein
Anheben auf 140.0 würde ESR 115 und ESR 128 ausschließen. web-ext lint meldet
dafür zwei Warnungen — die blockieren die Signierung nicht.
Installation auf Windows (per SSH)
deploy/deploy-windows.sh zählt die Version hoch, baut das .xpi, überträgt es per
scp und führt deploy/Install-SwyxTabBridge.ps1 auf dem Zielrechner aus:
./deploy/deploy-windows.sh # Testrechner, ohne Parameter
./deploy/deploy-windows.sh --keep-firefox # laufenden Firefox nicht beenden
./deploy/deploy-windows.sh --uninstall # rückgängig machen
Der nackte Aufruf erledigt alles: Version hochzählen, bei AMO signieren, übertragen, Policy schreiben und den laufenden Firefox beenden (nur den aus dem Zielverzeichnis — eine zweite Installation daneben bleibt offen). Ziel ist die Release-Installation, die ausschließlich signierte Add-ons annimmt; jeder Lauf wird deshalb signiert. Ziel-Rechner, Firefox-Verzeichnis sowie Signatur- und Neustart-Verhalten sind als Konstanten oben im Skript voreingestellt.
Standardmäßig wird eine Enterprise-Policy (force_installed) geschrieben, sodass das
Add-on beim nächsten Firefox-Start ohne Rückfrage für alle Profile installiert wird.
Details, Signierung und Optionen: deploy/README.md.
Tests
Selbsttest der Kommandoverarbeitung ohne Firefox — background.js läuft mit gestubbten
browser-/WebSocket-APIs:
node test-server/test-background.mjs
Testserver, der SwyxTray simuliert (ohne Abhängigkeiten, nur Node):
node test-server/server.js # lauscht auf ws://127.0.0.1:17655
node test-server/server.js --log /pfad/x.log # anderes Protokoll
node test-server/server.js --no-log # nur Konsole
Der Server protokolliert Verbindungsaufbau, Anmeldung und Trennung mit Zeitstempel
auf der Konsole und in test-server/swyx-tray.log:
[2026-08-17 16:16:09.809] Verbindung geöffnet von 127.0.0.1.
[2026-08-17 16:16:09.809] Angemeldet: swyx-tab-bridge v1.0.12 (Aktionen: list, open, close, activate, focus; ohne Token)
[2026-08-17 16:16:12.314] Verbindung getrennt: swyx-tab-bridge v1.0.12 (Dauer 3 s).
Meldet sich eine Gegenstelle binnen drei Sekunden nicht mit hello an, wird das
als „unbekannte Gegenstelle" vermerkt. Das ist die Vorlage dafür, was SwyxTray
mitschreiben sollte.
| Eingabe | gesendete Nachricht |
|---|---|
list / list current |
{"type":"tab","action":"list"} |
open <url> [background] |
{"type":"tab","action":"open","url":"…"} |
close <tabId> |
{"type":"tab","action":"close","tabId":42} |
activate <tabId> |
{"type":"tab","action":"activate","tabId":42} |
focus <url> [titel…] |
{"type":"tab","action":"focus","url":"…"} |
raw {…} |
beliebiges JSON (ohne type:"tab" bleibt es unbeantwortet) |
quit |
beenden |
Einstellungen
Die Einstellungsseite zeigt oben, ob das Add-on mit der Tray-App verbunden ist — Zustand, Serveradresse, Zeitpunkt des Verbindungsaufbaus und der letzte Fehler, laufend aktualisiert. Dieselbe Anzeige gibt es kompakt im Popup.
Über das Symbol in der Symbolleiste → Einstellungen:
- WebSocket-Server —
ws://…oderwss://…(Defaultws://127.0.0.1:17655) - Token — optionales Shared Secret. Ist es gesetzt, geht es in der
hello-Nachricht mit. - Automatisch verbinden — Auto-Reconnect mit exponentiellem Backoff (1 s → max. 30 s)
Das Symbol zeigt den Verbindungszustand: kein Badge = verbunden, … = Verbindungsaufbau,
! = getrennt.
Dateien
| Datei | Zweck |
|---|---|
manifest.json |
Add-on-Manifest (MV2, persistenter Hintergrund) |
background.js |
WebSocket-Client, Reconnect, Aktionen |
popup.html/js |
Statusanzeige, Verbinden/Trennen |
options.html/js |
Server-URL, Token, Auto-Connect |
test-server/server.js |
Testserver (simuliert SwyxTray) |
test-server/test-background.mjs |
Selbsttest der Kommandoverarbeitung |
deploy/deploy-windows.sh |
Version hochzaehlen, bauen, per SSH ausrollen |
deploy/Install-SwyxTabBridge.ps1 |
Installation auf dem Windows-Rechner |
Hinweise
- Manifest V2 ist bewusst gewählt: Nur mit persistentem Hintergrundskript bleibt die WebSocket-Verbindung dauerhaft offen. Unter MV3 (nicht-persistente Event-Page) würde die Verbindung nach Leerlauf abgebaut. Firefox unterstützt MV2 weiterhin.
ws://127.0.0.1ist erlaubt (das Add-on läuft immoz-extension:-Kontext, keine Mixed-Content-Blockade). Für Verbindungen über das Netzwerkwss://verwenden.- SwyxTray sollte nur auf
127.0.0.1lauschen — sonst kann jeder im Netz Tabs öffnen, schließen und die Tab-Liste mitlesen. - Die Permission
tabsist nötig, umtitleundurlder Tabs zu lesen.