Async Clipboard API 中的 HTML 未經處理

在 Chrome 120 中,非同步剪貼簿提供新的 unsanitized 選項 也能使用 Google Cloud CLI 或 Compute Engine API如果 HTML 程式碼符合某些特殊情況, 貼上與複製時相同的剪貼簿內容。 也就是說,沒有瀏覽器常見的中介清理步驟, 歡迎參閱本指南,瞭解如何使用這項功能。

使用 Async Clipboard API, 大多數情況下,開發人員不必擔心 就能假設他們會「寫入」到 剪貼簿 (複製) 與他們讀取資料後會看到的「相同」內容 貼上。

文字的確是如此。請試著將下列程式碼貼入開發人員工具 ,然後立即重新對焦頁面。(setTimeout() 為必要項目 讓您有足夠的時間來專注於網頁, 剪貼簿 API)。如您所見,輸入內容與輸出內容「完全」相同。

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

圖片則略有不同。避免來電 壓縮炸彈攻擊、瀏覽器 會對 PNG 等圖片進行重新編碼,但輸入和輸出影像在視覺上 每像素一個像素

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

但 HTML 文字會怎麼樣?您可能已經猜到,使用 HTML 情況則不同這時瀏覽器會清理 HTML 程式碼 例如從 HTML 程式碼中移除 <script> 標記 程式碼 (以及 <meta><head><style> 等其他程式碼),以及內嵌 CSS。 請參考以下範例,然後在開發人員工具控制台中試用。您將 會發現輸出內容與輸入內容有很大的差異

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

HTML 清理通常是個好事。您不想透露自己 在大多數情況下允許未經處理的 HTML 檔案來解決安全性問題。有 的情況是,但開發人員明確知道他們正在做什麼 因為這點和輸出 HTML 的完整性 應用程式功能的運作方式在這種情況下,您有以下兩種選擇:

  1. 如果您同時控管複製和貼上作業,例如: 再貼到應用程式中,應該使用 Async Clipboard API 的網頁自訂格式。 請停止閱讀以便查看連結的文章。
  2. 如果您只在應用程式中控制貼上功能,而不能控制複製的結束, 原因可能是複製作業是在不支援 網頁自訂格式,則應使用 unsanitized 選項,也就是 其他類似的說明。

包括移除 script 標記、內嵌樣式和 確認 HTML 格式正確這份清單僅列舉部分項目 步驟。

複製及貼上未經處理的 HTML

使用 Async Clipboard API write() (將) HTML 複製到剪貼簿時, 瀏覽器會透過 DOM 剖析器執行 並將產生的 HTML 字串序列化,但未在 這個步驟。您無需採取任何行動。當您將 HTML 放在 read() 上 另一個應用程式,而且你的網頁應用程式選擇 且需要在自己的程式碼中執行任何掃毒程序。 您可以利用屬性將選項物件傳遞至 read() 方法 unsanitized,值為 ['text/html']。在這裡,程式碼會如下所示: navigator.clipboard.read({ unsanitized: ['text/html'] })。以下程式碼範例 下方與先前顯示的結果幾乎相同,但這次使用的是 unsanitized 如果有需要 SQL 指令的分析工作負載 則 BigQuery 可能是最佳選擇在開發人員工具控制台中試用時,您會看到輸入 輸出內容相同

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

瀏覽器支援和功能偵測

但使用者無法直接檢查這項功能是否支援 便會被觀察。因此,以下範例 需要偵測 <style> 標記是否存續, 表示支援或內嵌,這表示未支援。請注意, 如要執行這項動作,頁面必須先取得剪貼簿 權限。

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

示範

如要瞭解 unsanitized 選項的實際運作情形,請參閱 Glitch 的示範,並觀看 原始碼

結論

如簡介所述,大部分的開發人員都不需要擔心 清理剪貼簿,並可使用預設的清理選項 產生的呼叫。在極少數情況下,開發人員必須注意 「unsanitized」選項已存在。

特別銘謝

本文經過 Anupam Snigdha 審查 Rachel Andrew。已指定 API 。