ตั้งแต่ Chrome 120 เป็นต้นไป ตัวเลือก unsanitized
ใหม่พร้อมให้ใช้งานใน API คลิปบอร์ดแบบไม่พร้อมกัน ตัวเลือกนี้สามารถช่วยในกรณีพิเศษด้วย HTML ซึ่งคุณต้องวางเนื้อหาของคลิปบอร์ดให้เหมือนกับตอนที่คัดลอกมา
กล่าวคือ ไม่ต้องใช้ขั้นตอนการทำความสะอาดข้อมูลขั้นกลางที่เบราว์เซอร์มักใช้กัน และเพื่อเหตุผลที่ดี ดูวิธีใช้งานได้ในคู่มือนี้
เมื่อใช้ Async Clipboard API ในกรณีส่วนใหญ่ นักพัฒนาซอฟต์แวร์ไม่จำเป็นต้องกังวลเกี่ยวกับความสมบูรณ์ของเนื้อหาในคลิปบอร์ดและสรุปได้ว่าเนื้อหาที่เขียนลงในคลิปบอร์ด (สำเนา) คือสิ่งที่เดียวกันจะได้รับเมื่ออ่านข้อมูลจากคลิปบอร์ด (วาง)
กรณีนี้เป็นจริงสำหรับข้อความ ลองวางโค้ดต่อไปนี้ในคอนโซล DevTools จากนั้นปรับโฟกัสหน้านั้นใหม่ทันที (จำเป็นต้องใช้ setTimeout()
เพื่อให้คุณมีเวลามากพอที่จะโฟกัสกับหน้าเว็บ ซึ่งเป็นข้อกำหนดของ Async Clipboard 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 เพื่อป้องกันไม่ให้สิ่งไม่ดีเกิดขึ้น เช่น ตัดแท็ก <script>
ออกจากโค้ด HTML (และแท็กอื่นๆ อย่าง <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 ในและเอาต์พุตมีความสำคัญต่อการทำงานที่ถูกต้องของแอป ในสถานการณ์เช่นนี้ คุณมี 2 ตัวเลือก ได้แก่
- ตัวอย่างเช่น หากคุณควบคุมทั้งการคัดลอกและจุดสิ้นสุดการวาง เช่น หากคัดลอกจากภายในแอปเพื่อวางภายในแอปเช่นเดียวกัน คุณควรใช้รูปแบบเว็บที่กำหนดเองสำหรับ Async Clipboard API หยุดอ่านที่นี่และตรวจสอบบทความที่ลิงก์
- หากคุณควบคุมเฉพาะส่วนสุดท้ายของการวางในแอป แต่ไม่อนุญาตให้คัดลอก อาจเป็นเพราะการดำเนินการคัดลอกเกิดขึ้นในแอปที่มาพร้อมเครื่องที่ไม่รองรับรูปแบบเว็บที่กำหนดเอง คุณควรใช้ตัวเลือก
unsanitized
ซึ่งอธิบายไว้ในส่วนที่เหลือของบทความนี้
การทำความสะอาดรวมถึงสิ่งต่างๆ เช่น การนำแท็ก script
ออก รูปแบบในบรรทัด และการดูแลให้ HTML มีรูปแบบที่ถูกต้อง รายการนี้ไม่ครอบคลุมและเราอาจเพิ่มขั้นตอนอื่นๆ ในอนาคต
คัดลอกและวาง HTML ที่ไม่ผ่านการตรวจสอบ
เมื่อคุณ write()
(คัดลอก) HTML ไปยังคลิปบอร์ดด้วย API คลิปบอร์ดแบบไม่พร้อมกัน เบราว์เซอร์จะตรวจสอบว่ามีการจัดรูปแบบอย่างถูกต้องโดยการเรียกใช้ผ่านโปรแกรมแยกวิเคราะห์ DOM และเรียงอันดับสตริง HTML ที่ได้ แต่ขั้นตอนนี้ไม่มีการทำความสะอาดเกิดขึ้น คุณไม่ต้องดำเนินการใดๆ เมื่อนำ HTML ของ read()
ไปวางในคลิปบอร์ดโดยแอปพลิเคชันอื่น และเว็บแอปเลือกรับเนื้อหาที่มีความแม่นยำทั้งหมด และจำเป็นต้องดำเนินการทำความสะอาดในโค้ดของคุณเอง คุณสามารถส่งออบเจ็กต์ตัวเลือกไปยังเมธอด read()
ที่มีพร็อพเพอร์ตี้ unsanitized
และค่า ['text/html']
การแยกออกมาจะมีลักษณะดังนี้
navigator.clipboard.read({ unsanitized: ['text/html'] })
ตัวอย่างโค้ดต่อไปนี้ด้านล่างแทบจะเหมือนกับตัวอย่างที่แสดงก่อนหน้านี้ แต่คราวนี้มีตัวเลือก unsanitized
เมื่อลองใช้ในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บแล้ว คุณจะเห็นว่าอินพุตและเอาต์พุตเหมือนกัน
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 ทีม Microsoft Edge เป็นผู้ระบุ และใช้งาน API