Chrome-Inkompatibilitäten
Die WebExtension-APIs zielen darauf ab, Kompatibilität über alle gängigen Browser hinweg bereitzustellen, sodass Erweiterungen in jedem Browser mit minimalen Änderungen laufen sollten.
Es gibt jedoch erhebliche Unterschiede zwischen Chrome (und auf Chromium basierenden Browsern), Firefox und Safari. Insbesondere:
-
Die Unterstützung für WebExtension-APIs variiert zwischen den Browsern. Siehe Unterstützung von JavaScript-APIs in Browsern für Details.
-
Die Unterstützung für
manifest.json
-Schlüssel variiert zwischen den Browsern. Siehe den Abschnitt "Browser-Kompatibilität" auf der Seitemanifest.json
für weitere Details. -
Erweiterungs-API-Namespace:
- In Firefox und Safari: Erweiterungs-APIs werden unter dem
browser
-Namespace aufgerufen. Derchrome
-Namespace wird ebenfalls für die Kompatibilität mit Chrome unterstützt. - In Chrome: Erweiterungs-APIs werden unter dem
chrome
-Namespace aufgerufen. (vgl. Chrome Bug 798169)
- In Firefox und Safari: Erweiterungs-APIs werden unter dem
-
Asynchrone APIs:
- In Firefox und Safari: Asynchrone APIs werden mit Promises implementiert.
- In Chrome: In Manifest V2 werden asynchrone APIs mit Rückruffunktionen implementiert. In Manifest V3 wird die Unterstützung für Promises bei den meisten geeigneten Methoden bereitgestellt. (vgl. Chrome Bug 328932) Rückruffunktionen werden in Manifest V3 für die Abwärtskompatibilität unterstützt.
Der Rest dieser Seite beschreibt diese und andere Inkompatibilitäten im Detail.
JavaScript-APIs
chrome.* und browser.* Namespace
-
In Firefox und Safari: Die APIs werden mit dem
browser
-Namespace aufgerufen.jsbrowser.browserAction.setIcon({ path: "path/to/icon.png" });
-
In Chrome: Die APIs werden mit dem
chrome
-Namespace aufgerufen.jschrome.browserAction.setIcon({ path: "path/to/icon.png" });
Callbacks und Promises
-
In Firefox und Safari (alle Versionen) und Chrome (ab Manifest Version 3): Asynchrone APIs verwenden Promises, um Werte zurückzugeben.
jsfunction logCookie(c) { console.log(c); } function logError(e) { console.error(e); } let setCookie = browser.cookies.set({ url: "https://developer.mozilla.org/", }); setCookie.then(logCookie, logError);
-
In Chrome: In Manifest V2 verwenden asynchrone APIs Rückruffunktionen, um Werte zurückzugeben und
runtime.lastError
, um Fehler mitzuteilen. In Manifest V3 werden Rückruffunktionen für die Rückwärtskompatibilität unterstützt, zusammen mit der Unterstützung für Promises bei den meisten geeigneten Methoden.jsfunction logCookie(c) { if (chrome.runtime.lastError) { console.error(chrome.runtime.lastError); } else { console.log(c); } } chrome.cookies.set({ url: "https://developer.mozilla.org/" }, logCookie);
Firefox unterstützt sowohl die chrome- als auch browser-Namespaces
Als Hilfe beim Portieren unterstützt die Firefox-Implementierung von WebExtensions chrome
unter Verwendung von Rückrufen und browser
unter Verwendung von Promises. Dies bedeutet, dass viele Chrome-Erweiterungen in Firefox ohne Änderungen funktionieren.
Hinweis: Der browser
-Namespace wird von Firefox und Safari unterstützt. Chrome bietet den browser
-Namespace nicht an, bis Chrome bug 798169 gelöst ist.
Wenn Sie sich entscheiden, Ihre Erweiterung so zu schreiben, dass sie browser
und Promises verwendet, bietet Firefox ein Polyfill, das es ermöglichen sollte, sie in Chrome auszuführen: https://github.com/mozilla/webextension-polyfill.
Teilweise unterstützte APIs
Die Seite Unterstützung von JavaScript-APIs in Browsern enthält Kompatibilitätstabellen für alle APIs, die in Firefox Unterstützung haben. Wo es Einschränkungen bezüglich der Unterstützung einer API-Methode, eines Attributs, Typs oder Ereignisses gibt, wird dies in diesen Tabellen mit einem Sternchen "*" angezeigt. Wenn Sie das Sternchen auswählen, wird die Tabelle erweitert, um eine Erklärung der Einschränkung anzuzeigen.
Die Tabellen werden aus Kompatibilitätsdaten generiert, die als JSON-Dateien in GitHub gespeichert sind.
Der Rest dieses Abschnitts beschreibt die Hauptkompatibilitätsprobleme, die Sie berücksichtigen müssen, wenn Sie eine browserübergreifende Erweiterung erstellen. Vergessen Sie nicht, die Browser-Kompatibilitätstabellen zu überprüfen, da sie möglicherweise zusätzliche Kompatibilitätsinformationen enthalten.
Notifications API
Für notifications.create()
mit type "basic"
:
- In Firefox:
iconUrl
ist optional. - In Chrome:
iconUrl
ist erforderlich.
Wenn der Benutzer auf eine Benachrichtigung klickt:
- In Firefox: Die Benachrichtigung wird sofort gelöscht.
- In Chrome: Dies ist nicht der Fall.
Bei mehrmaligem Aufruf von notifications.create()
in schneller Folge:
- In Firefox: Die Benachrichtigungen werden möglicherweise nicht angezeigt. Das Warten auf die nächsten Aufrufe innerhalb der
notifications.create()
-Rückruffunktion ist keine ausreichende Verzögerung, um dies zu verhindern.
Proxy API
Firefox und Chrome enthalten eine Proxy-API. Die Gestaltung dieser beiden APIs ist jedoch nicht kompatibel.
- In Firefox: Proxies werden mit der Eigenschaft proxy.settings oder proxy.onRequest eingestellt, um ProxyInfo dynamisch bereitzustellen. Siehe proxy für weitere Informationen zur API.
- In Chrome: Proxy-Einstellungen werden in einem
proxy.ProxyConfig
-Objekt definiert. Abhängig von den Proxy-Einstellungen von Chrome können die Einstellungenproxy.ProxyRules
oder einproxy.PacScript
enthalten. Proxies werden mit der Eigenschaft proxy.settings eingestellt. Siehe chrome.proxy für weitere Informationen zur API.
Tabs API
Beim Verwenden von tabs.executeScript()
oder tabs.insertCSS()
:
- In Firefox: Relative URLs werden relativ zur aktuellen Seiten-URL aufgelöst.
- In Chrome: Relative URLs werden relativ zur Basis-URL der Erweiterung aufgelöst.
Um browserübergreifend zu arbeiten, können Sie den Pfad als absolute URL angeben, beginnend am Stamm der Erweiterung, wie folgt:
/path/to/script.js
Beim Aufrufen von tabs.remove()
:
- In Firefox: Das
tabs.remove()
-Promise wird nach dembeforeunload
-Ereignis erfüllt. - In Chrome: Der Rückruf wartet nicht auf
beforeunload
.
WebRequest API
-
In Firefox:
-
Anfragen können nur umgeleitet werden, wenn die ursprüngliche URL das Schema
http:
oderhttps:
verwendet. -
Die Berechtigung
activeTab
erlaubt nicht das Abfangen von Netzwerkanfragen im aktuellen Tab. (Siehe bug 1617479) -
Ereignisse werden für Systemanfragen (zum Beispiel Erweiterungs-Updates oder Suchleisten-Vorschläge) nicht ausgelöst.
- Ab Firefox 57: Firefox macht eine Ausnahme für Erweiterungen, die
webRequest.onAuthRequired
für Proxy-Authentifizierung abfangen müssen. Siehe die Dokumentation zuwebRequest.onAuthRequired
.
- Ab Firefox 57: Firefox macht eine Ausnahme für Erweiterungen, die
-
Wenn eine Erweiterung eine öffentliche (z.B. HTTPS) URL zu einer Erweiterungsseite umleiten möchte, muss die
manifest.json
-Datei der Erweiterung einenweb_accessible_resources
-Schlüssel mit der URL der Erweiterungsseite enthalten.Hinweis: Jede Website kann auf diese URL verlinken oder umleiten, und Erweiterungen sollten jeden Eingabewert (z.B. POST-Daten) so behandeln, als ob er von einer nicht vertrauenswürdigen Quelle stammt, genauso wie eine normale Webseite.
-
Einige der
browser.webRequest.*
-APIs erlauben das Zurückgeben von Promises, diewebRequest.BlockingResponse
asynchron auflösen.
-
-
In Chrome: Nur
webRequest.onAuthRequired
unterstützt asynchroneswebRequest.BlockingResponse
durch die Bereitstellung von'asyncBlocking'
über einen Rückruf anstelle eines Promises.
Windows API
- In Firefox:
onFocusChanged
des APIwindows
wird mehrfach bei einer Fokusänderung ausgelöst.
Nicht unterstützte APIs
DeclarativeContent API
Verschiedene Inkompatibilitäten
URLs in CSS
- In Firefox: URLs in injizierten CSS-Dateien werden relativ zur CSS-Datei aufgelöst.
- In Chrome: URLs in injizierten CSS-Dateien werden relativ zur Seite, in die sie injiziert werden aufgelöst.
Unterstützung für Dialoge in Hintergrundseiten
web_accessible_resources
- In Firefox: Ressourcen erhalten eine zufällige UUID, die für jede Instanz von Firefox ändert:
moz-extension://«random-UUID»/«path»
. Diese Zufälligkeit kann verhindern, dass Sie Dinge tun können, wie zum Beispiel die URL Ihrer Erweiterung zur CSP-Richtlinie einer anderen Domain hinzuzufügen. - In Chrome: Wenn eine Ressource in
web_accessible_resources
aufgeführt ist, ist sie zugänglich alschrome-extension://«your-extension-id»/«path»
. Die Erweiterungs-ID ist für eine Erweiterung festgelegt.
Manifest "key"-Eigenschaft
- In Firefox: Da Firefox zufällige UUIDs für
web_accessible_resources
verwendet, wird diese Eigenschaft nicht unterstützt. Firefox-Erweiterungen können ihre Erweiterungs-ID über denbrowser_specific_settings.gecko.id
Manifest-Schlüssel fixieren (siehe browser_specific_settings.gecko). - In Chrome: Wenn Sie mit einer nicht verpackten Erweiterung arbeiten, kann das Manifest eine
"key"
-Eigenschaft enthalten, um die Erweiterungs-ID auf verschiedenen Rechnern zu fixieren. Dies ist hauptsächlich nützlich, wenn Sie mitweb_accessible_resources
arbeiten.
HTTP(S)-Anfragen von Content Scripts
- In Firefox: Wenn ein Content Script eine HTTP(S)-Anfrage macht, müssen Sie absolute URLs angeben.
- In Chrome: Wenn ein Content Script eine Anfrage (zum Beispiel mit
fetch()
) an eine relative URL (wie/api
) macht, wird sie anhttps://example.com/api
gesandt.
Content Script Umgebung
- In Firefox: Der globale Scope der Content Script Umgebung ist nicht strikt gleich
window
(Firefox Bug 1208775). Genauer gesagt, ist der globale Scope (globalThis
) aus standardmäßigen JavaScript-Features zusammengesetzt, pluswindow
als Prototyp des globalen Scopes. Die meisten DOM-APIs werden von der Seite überwindow
geerbt, durch Xray Vision, um das Content Script vor Modifikationen durch die Webseite zu schützen. Ein Content Script kann JavaScript-Objekte aus seinem globalen Scope oder Xray-umwickelte Versionen von der Webseite treffen. - In Chrome: Der globale Scope ist
window
, und die verfügbaren DOM-APIs sind im Allgemeinen unabhängig von der Webseite (außer dem Teilen des zugrunde liegenden DOMs). Content Scripts können nicht direkt auf JavaScript-Objekte von der Webseite zugreifen.
Code in einer Webseite aus Content Script ausführen
- In Firefox:
eval
führt Code im Kontext des Content Scripts aus undwindow.eval
führt Code im Kontext der Seite aus. Siehe Verwendung voneval
in Content Scripts. - In Chrome:
eval
undwindow.eval
führen immer Code im Kontext des Content Scripts aus, nicht im Kontext der Seite.
Variablen zwischen Content Scripts teilen
- In Firefox: Sie können keine Variablen zwischen Content Scripts teilen, indem Sie sie in einem Skript
this.{variableName}
zuweisen und dann versuchen, sie in einem anderen Skript mitwindow.{variableName}
zuzugreifen. Dies ist eine Beschränkung, die durch die Sandbox-Umgebung in Firefox erstellt wurde. Diese Einschränkung könnte entfernt werden; siehe Firefox Bug 1208775.
Lebenszyklus von Content Scripts während der Navigation
-
In Firefox: Content Scripts bleiben in einer Webseite injiziert, nachdem der Benutzer weggegangen ist. Jedoch werden die Eigenschaften des Fensterobjekts zerstört. Beispiel: Wenn ein Content Script
window.prop1 = "prop"
setzt und der Benutzer dann weg navigiert und zurück zur Seite kommt, istwindow.prop1
undefiniert. Dieses Problem wird im Firefox Bug 1525400 verfolgt.Um das Verhalten von Chrome zu simulieren, horchen Sie auf die pageshow und pagehide Ereignisse. Simulieren Sie dann das Injizieren oder Zerstören des Content Scripts.
-
In Chrome: Content Scripts werden zerstört, wenn der Benutzer von einer Webseite weg navigiert. Wenn der Benutzer den Zurück-Knopf drückt, um durch die Historie auf die Seite zurückzukehren, wird das Content Script in die Webseite injiziert.
"per-tab" Zoom-Verhalten
- In Firefox: Das Zoom-Level bleibt über Seitenladevorgänge und Navigation innerhalb des Tabs bestehen.
- In Chrome: Zoom-Änderungen werden beim Navigieren zurückgesetzt; ein Tab lädt immer Seiten mit ihren per-Origin-Zoomfaktoren.
Siehe tabs.ZoomSettingsScope
.
manifest.json Schlüssel
Die Hauptseite manifest.json
enthält eine Tabelle, die die Unterstützung von manifest.json
-Schlüsseln in Browsern beschreibt. Wo es Einschränkungen bezüglich der Unterstützung eines bestimmten Schlüssels gibt, wird dies in der Tabelle mit einem Sternchen "*" angezeigt. Wenn Sie das Sternchen auswählen, wird die Tabelle erweitert, um eine Anmerkung zu der Einschränkung anzuzeigen.
Die Tabellen werden aus Kompatibilitätsdaten generiert, die als JSON-Dateien in GitHub gespeichert sind.
Native Messaging
Verbindungsbasierte Messaging-Argumente
Auf Linux und Mac: Chrome übergibt ein Argument an die native App, welches der Ursprung der Erweiterung ist, die sie gestartet hat, in Form von chrome-extension://«extensionID/»
(nachgestellter Schrägstrich erforderlich). Dies ermöglicht der App, die Erweiterung zu identifizieren.
Auf Windows: Chrome übergibt zwei Argumente:
- Den Ursprung der Erweiterung
- Ein Handle auf das nativen Chrome-Fenster, das die App gestartet hat
allowed_extensions
- In Firefox: Der Manifest-Schlüssel heißt
allowed_extensions
. - In Chrome: Der Manifest-Schlüssel heißt
allowed_origins
.
App Manifest Standort
- In Chrome: Das App-Manifest wird an einer anderen Stelle erwartet. Siehe Standort des nativen Messaging-Hosts in den Chrome-Dokumenten.
App-Persistenz
- In Firefox: Wenn eine Native Messaging-Verbindung geschlossen wird, beendet Firefox die Subprozesse, falls sie sich nicht trennen. Unter Windows setzt der Browser den Prozess der nativen Anwendung in ein Job Objekt und beendet den Job. Nehmen wir an, die native Anwendung startet andere Prozesse und möchte, dass sie offen bleiben, nachdem die native Anwendung beendet ist. In diesem Fall muss die native Anwendung
CreateProcess
verwenden, anstatt vonShellExecute
den zusätzlichen Prozess mit demCREATE_BREAKAWAY_FROM_JOB
-Flag zu starten.
Datenklonierungsalgorithmus
Einige Erweiterungs-APIs erlauben es einer Erweiterung, Daten von einem Teil der Erweiterung zu einem anderen zu senden, wie z.B. runtime.sendMessage()
, tabs.sendMessage()
, runtime.onMessage
, die postMessage()
-Methode von runtime.port
, und tabs.executeScript()
.
- In Firefox: Der Strukturierte Klonalgoithmus wird verwendet.
- In Chrome: Der JSON-Serialisierungsalgorithmus wird verwendet. Es könnte in Zukunft auf strukturiertes Klonen umgestellt werden (Issue 248548).
Der strukturierte Klonalgoithmus unterstützt mehr Typen als der JSON-Serialisierungsalgorithmus. Eine bemerkenswerte Ausnahme sind (DOM)-Objekte mit einer toJSON
-Methode. DOM-Objekte sind nicht standardmäßig klonbar oder JSON-serialisierbar, aber mit einer toJSON()
-Methode können diese JSON-serialisierbar sein (aber immer noch nicht mit dem strukturierten Klonalgoithmus geklont werden). Beispiele für JSON-serialisierbare Objekte, die nicht strukturiert klonbar sind, beinhalten Instanzen von URL
und PerformanceEntry
.
Erweiterungen, die auf der toJSON()
-Methode des JSON-Serialisierungsalgorithmus basieren, können JSON.stringify()
gefolgt von JSON.parse()
nutzen, um sicherzustellen, dass eine Nachricht ausgetauscht werden kann, da ein geparster JSON-Wert immer strukturell klonbar ist.