Ab Chrome 120 ist in der Async Clipboard API eine neue unsanitized
-Option verfügbar. Diese Option kann in besonderen Situationen mit HTML hilfreich sein, wenn Sie den Inhalt der Zwischenablage genau so einfügen müssen, wie er beim Kopieren war.
Das heißt, ohne Zwischenschritt zur Bereinigung, den Browser normalerweise aus guten Gründen anwenden. In diesem Leitfaden erfahren Sie, wie Sie die Funktion verwenden.
Bei der Arbeit mit der Async Clipboard API müssen sich Entwickler in den meisten Fällen keine Gedanken über die Integrität der Inhalte in der Zwischenablage machen. Sie können davon ausgehen, dass das, was sie in die Zwischenablage schreiben (kopieren), dasselbe ist, was sie erhalten, wenn sie die Daten aus der Zwischenablage lesen (einfügen).
Das gilt auf jeden Fall für Text. Fügen Sie den folgenden Code in die DevTools-Konsole ein und fokussieren Sie die Seite dann sofort wieder. Die setTimeout()
ist erforderlich, damit Sie genügend Zeit haben, die Seite zu fokussieren. Dies ist eine Anforderung der Async Clipboard API. Wie Sie sehen, ist die Eingabe genau dieselbe wie die Ausgabe.
setTimeout(async () => {
const input = 'Hello';
await navigator.clipboard.writeText(input);
const output = await navigator.clipboard.readText();
console.log(input, output, input === output);
// Logs "Hello Hello true".
}, 3000);
Bei Bildern ist das etwas anders. Um sogenannte Compression Bomb-Angriffe zu verhindern, codieren Browser Bilder wie PNGs neu. Die Eingabe- und Ausgabebilder sind jedoch visuell pixelgenau identisch.
setTimeout(async () => {
const dataURL =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=';
const input = await fetch(dataURL).then((response) => response.blob());
await navigator.clipboard.write([
new ClipboardItem({
[input.type]: input,
}),
]);
const [clipboardItem] = await navigator.clipboard.read();
const output = await clipboardItem.getType(input.type);
console.log(input.size, output.size, input.type === output.type);
// Logs "68 161 true".
}, 3000);
Was passiert mit HTML-Text? Wie Sie vielleicht vermutet haben, ist die Situation bei HTML anders. Hier bereinigt der Browser den HTML-Code, um beispielsweise <script>
-Tags (und andere wie <meta>
, <head>
und <style>
) aus dem HTML-Code zu entfernen und CSS inline zu verwenden.
Sehen Sie sich das folgende Beispiel an und probieren Sie es in der DevTools-Konsole aus. Sie werden feststellen, dass sich die Ausgabe erheblich von der Eingabe unterscheidet.
setTimeout(async () => {
const input = `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="ProgId" content="Excel.Sheet" />
<meta name="Generator" content="Microsoft Excel 15" />
<style>
body {
font-family: HK Grotesk;
background-color: var(--color-bg);
}
</style>
</head>
<body>
<div>hello</div>
</body>
</html>`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read();
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
console.log(input, output);
}, 3000);
Die HTML-Bereinigung ist im Allgemeinen eine gute Sache. Sie möchten sich nicht Sicherheitsrisiken aussetzen, indem Sie in den meisten Fällen nicht bereinigtes HTML zulassen. Es gibt jedoch Szenarien, in denen der Entwickler genau weiß, was er tut, und in denen die Integrität des Ein- und Ausgabe-HTML für die korrekte Funktion der App entscheidend ist. Unter diesen Umständen haben Sie zwei Möglichkeiten:
- Wenn Sie sowohl das Kopieren als auch das Einfügen steuern, z. B. wenn Sie in Ihrer App kopieren und dann ebenfalls in Ihrer App einfügen, sollten Sie benutzerdefinierte Webformate für die Async Clipboard API verwenden. Lesen Sie nicht weiter, sondern sehen Sie sich den verlinkten Artikel an.
- Wenn Sie nur das Einfügen in Ihrer App, nicht aber das Kopieren steuern können, z. B. weil der Kopiervorgang in einer nativen App erfolgt, die keine benutzerdefinierten Webformate unterstützt, sollten Sie die Option
unsanitized
verwenden, die im weiteren Verlauf dieses Artikels beschrieben wird.
Die Bereinigung umfasst beispielsweise das Entfernen von script
-Tags, das Inlinestyling und das Sicherstellen, dass das HTML-Dokument wohlgeformt ist. Diese Liste ist nicht vollständig und es können in Zukunft weitere Schritte hinzukommen.
Kopieren und Einfügen von nicht bereinigtem HTML-Code
Wenn Sie mit der Async Clipboard API HTML in die Zwischenablage write()
(kopieren), sorgt der Browser dafür, dass es wohlgeformt ist, indem er es durch einen DOM-Parser leitet und den resultierenden HTML-String serialisiert. In diesem Schritt erfolgt jedoch keine Bereinigung. Sie müssen nichts weiter tun. Wenn Sie read()
-HTML-Code, der von einer anderen Anwendung in die Zwischenablage kopiert wurde, und Ihre Web-App die Inhalte in voller Qualität abrufen und alle Bereinigungen in Ihrem eigenen Code durchführen muss, können Sie ein Optionenobjekt mit dem Attribut unsanitized
und dem Wert ['text/html']
an die read()
-Methode übergeben. Isoliert betrachtet sieht sie so aus:
navigator.clipboard.read({ unsanitized: ['text/html'] })
. Das folgende Codebeispiel ist fast dasselbe wie das zuvor gezeigte, aber dieses Mal mit der Option unsanitized
. Wenn Sie es in der Entwicklertools-Konsole ausprobieren, sehen Sie, dass Eingabe und Ausgabe identisch sind.
setTimeout(async () => {
const input = `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="ProgId" content="Excel.Sheet" />
<meta name="Generator" content="Microsoft Excel 15" />
<style>
body {
font-family: HK Grotesk;
background-color: var(--color-bg);
}
</style>
</head>
<body>
<div>hello</div>
</body>
</html>`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read({
unsanitized: ['text/html'],
});
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
console.log(input, output);
}, 3000);
Browserunterstützung und Funktionserkennung
Es gibt keine direkte Möglichkeit, zu prüfen, ob die Funktion unterstützt wird. Die Erkennung von Funktionen basiert daher auf der Beobachtung des Verhaltens. Im folgenden Beispiel wird daher darauf geachtet, ob ein <style>
-Tag erhalten bleibt (was auf Unterstützung hinweist) oder inline eingefügt wird (was auf Nichtunterstützung hinweist). Damit das funktioniert, muss die Seite bereits die Berechtigung für die Zwischenablage haben.
const supportsUnsanitized = async () => {
const input = `<style>p{color:red}</style><p>a`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read({
unsanitized: ['text/html],
});
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
return /<style>/.test(output);
};
Demo
Zusammenfassung
Wie in der Einleitung beschrieben, müssen sich die meisten Entwickler keine Gedanken über die Bereinigung der Zwischenablage machen und können einfach mit den vom Browser vorgenommenen Standardbereinigungsoptionen arbeiten. Für die seltenen Fälle, in denen Entwickler sich darum kümmern müssen, gibt es die Option unsanitized
.
Nützliche Links
Danksagungen
Dieser Artikel wurde von Anupam Snigdha und Rachel Andrew geprüft. Die API wurde vom Microsoft Edge-Team spezifiziert und implementiert.