Code HTML non rectifié dans l'API Async Clipboard

À partir de Chrome 120, une nouvelle option unsanitized est disponible dans le presse-papiers asynchrone API. Cette option peut être utile dans certaines situations spéciales avec HTML, où vous devez collez le contenu du presse-papiers de la même façon qu'au moment de la copie. C'est-à-dire, sans étape de nettoyage intermédiaire que les navigateurs pour de bonnes raisons : postulez. Découvrez comment l'utiliser dans ce guide.

Lorsque vous travaillez avec API Async Clipboard dans la majorité des cas, les développeurs n'ont pas à se soucier de l'intégrité le contenu du presse-papiers et peut supposer que ce qu'ils écrivent sur le presse-papiers (copie) est identique à celle obtenue en lire les données depuis le presse-papiers (coller).

C'est sans aucun doute le cas pour le texte. Essayez de coller le code suivant dans les outils de développement dans la console, puis recentrez immédiatement la page. (Le setTimeout() est nécessaire Vous disposez ainsi de suffisamment de temps pour vous concentrer sur la page, ce qui est l'une des exigences API Clipboard). Comme vous pouvez le voir, l'entrée est exactement la même que la sortie.

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);

Avec les images, c'est un peu différent. Pour empêcher ce que l'on appelle les attaques par bombe de compression, les navigateurs réencodez des images comme au format PNG, mais les images d'entrée et de sortie sont visuellement exactement la même chose, pixel par pixel.

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);

Mais qu'en est-il du texte HTML ? Comme vous l'avez peut-être deviné, avec le langage HTML, la situation est différente. Ici, le navigateur nettoie le code HTML pour éviter en supprimant les balises <script> du code HTML, par exemple. (et d'autres éléments comme <meta>, <head> et <style>) et en intégrant le CSS. Examinez l'exemple suivant et essayez-le dans la console des outils de développement. Vous : notez que la sortie diffère très nettement de l'entrée.

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);

Le nettoyage HTML est généralement une bonne chose. Vous ne devez pas vous exposer aux problèmes de sécurité en autorisant le HTML non rectifié dans la majorité des cas. Il y sont des scénarios dans lesquels le développeur sait exactement ce qu'il fait et où l'intégrité du code HTML de l'entrée et de la sortie est cruciale le fonctionnement de l’application. Dans ce cas, deux possibilités s'offrent à vous:

  1. Si vous contrôlez à la fois la copie et le collage (par exemple, si vous copiez depuis votre application pour les coller dans celle-ci, vous devez utiliser Formats Web personnalisés pour l'API Async Clipboard Arrêtez la lecture et consultez l'article associé.
  2. Si vous contrôlez uniquement le collage dans votre application, peut-être parce que l'opération de copie a lieu dans une application native qui n'est pas des formats personnalisés pour le Web, vous devez utiliser l'option unsanitized, qui est expliqué dans la suite de cet article.

L'assainissement inclut, par exemple, la suppression des balises script, l'intégration de styles et de s'assurer que le code HTML est bien formé. Cette liste n'est pas exhaustive, et bien d'autres des étapes peuvent être ajoutées ultérieurement.

Copier et coller du code HTML non rectifié

Lorsque vous write() (copiez) du code HTML dans le presse-papiers avec l'API Async Clipboard, le navigateur s'assure qu'il est correctement formé en l'exécutant via un analyseur DOM et de sérialiser la chaîne HTML obtenue, mais aucun nettoyage n'a lieu au niveau cette étape. Vous n'avez rien à faire. Lorsque vous read() avez placé du code HTML presse-papiers par une autre application, et votre application Web active la fonction contenu ultraprécis et la nécessité de nettoyer votre propre code, vous pouvez transmettre un objet d'options à la méthode read() avec une propriété unsanitized et la valeur ['text/html']. Indépendamment, il se présente comme suit: navigator.clipboard.read({ unsanitized: ['text/html'] }) L'exemple de code suivant ci-dessous est presque identique à celle présentée précédemment, mais cette fois avec unsanitized . Lorsque vous l'essayerez dans la console DevTools, vous constaterez que l'entrée et la sortie est la même.

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);

Prise en charge des navigateurs et détection de fonctionnalités

Il n'existe aucun moyen direct de vérifier si la fonctionnalité est prise en charge. la détection repose sur l'observation du comportement. Par conséquent, l'exemple suivant repose sur la détection de la présence ou non d'une balise <style>, ce qui indique une compatibilité ou est en cours d'intégration, ce qui indique qu'il n'y a pas de support. Notez que Pour que cela fonctionne, la page doit déjà avoir obtenu le presse-papiers l'autorisation.

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);
};

Démo

Pour voir comment l'option unsanitized fonctionne, consultez la sur Glitch et regardez sa code source.

Conclusions

Comme indiqué dans l'introduction, la plupart des développeurs n'auront jamais à se soucier le nettoyage du presse-papiers et ne peut fonctionner qu'avec les options de nettoyage par défaut. effectuées par le navigateur. Dans les rares cas où les développeurs doivent se soucier, L'option unsanitized existe.

Remerciements

Cet article a été lu par Anupam Snigdha et Rachel Andrew. L'API a été spécifiée, par l'équipe Microsoft Edge.