4 คะแนน โดย GN⁺ 2024-09-02 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เว็บคลิปบอร์ดจัดเก็บข้อมูลที่คัดลอกหนึ่งครั้งไว้พร้อมกันใน การแทนค่า MIME หลายแบบ ทำให้แต่ละแอปเลือกวางเฉพาะรูปแบบที่ต้องการได้
  • async Clipboard API ถูกจำกัดไว้หลัก ๆ ที่ text/plain, text/html, image/png ทำให้ประเภทกำหนดเองอย่าง application/json ถูก ปฏิเสธการเขียนด้วยเหตุผลด้านความปลอดภัย
  • Clipboard Events API รุ่นเก่าสามารถจัดการสตริงประเภทใดก็ได้ แต่คลิปบอร์ดของระบบจริงจะเปลี่ยนได้เฉพาะภายใน เหตุการณ์จากผู้ใช้ที่เชื่อถือได้ เท่านั้น
  • Google Docs สร้างเหตุการณ์ copy ที่เชื่อถือได้ด้วย execCommand("copy") ส่วน Figma ใส่ข้อมูลกำหนดเองไว้ใน แอตทริบิวต์ HTML แบบ base64 เพื่อจัดการการคัดลอกระหว่างเบราว์เซอร์กับแอปเนทีฟ
  • Web Custom Formats เปิดให้ใช้ประเภทกำหนดเองใน async Clipboard API ด้วยพรีฟิกซ์ "web " แต่ต้องใช้เบราว์เซอร์ฐาน Chromium และแอปเนทีฟที่อัปเดตแล้ว

โมเดลพื้นฐานของเว็บคลิปบอร์ด

  • คลิปบอร์ดสามารถจัดเก็บ การแทนค่า (representation) หลายแบบพร้อม MIME type สำหรับการคัดลอกหนึ่งครั้งได้
    • text/plain: ข้อความธรรมดา
    • text/html: HTML
    • image/png: รูปภาพ PNG
  • Google Docs อ่านการแทนค่า text/html เพื่อรักษารูปแบบอย่างลิงก์ ขนาดตัวอักษร และสี
  • แอปที่ต้องการเฉพาะข้อความต้นฉบับอย่าง VS Code อ่านเพียงการแทนค่า text/plain ก็พอ

ข้อจำกัดด้านประเภทของ async Clipboard API

  • navigator.clipboard.read() ตรวจสอบ types ของรายการในคลิปบอร์ด แล้วอ่านการแทนค่าที่ต้องการ เช่น getType("text/html")
  • navigator.clipboard.write() สามารถใส่ Blob หลายตัวไว้ใน ClipboardItem ที่ใช้ MIME type เป็นคีย์ แล้วเขียนได้ในครั้งเดียว
  • หากพยายามเขียน application/json ด้วย ClipboardItem จะเกิดข้อยกเว้น Type application/json not supported on write
  • สเปก W3C Clipboard กำหนดให้ปฏิเสธประเภทที่ไม่ใช่ text/plain, text/html, image/png ในการ write
  • application/json เคยอยู่ในรายการประเภทข้อมูลบังคับตั้งแต่ปี 2012 ถึง 2021 แต่ถูกนำออกใน w3c/clipboard-apis#155
    • ก่อนเปลี่ยนแปลง มีประเภทข้อมูลบังคับสำหรับการอ่าน 16 ประเภท และสำหรับการเขียน 8 ประเภท
    • รายการถูกลดลงเพราะเบราว์เซอร์จำนวนมากไม่รองรับประเภทบังคับหลายประเภทด้วยความกังวลด้านความปลอดภัย
  • สเปกเตือนว่า สคริปต์ที่ไม่น่าเชื่อถืออาจใส่ข้อมูลที่กระตุ้นช่องโหว่ของซอฟต์แวร์ในเครื่องลงในคลิปบอร์ดได้ จึง จำกัดประเภทที่อนุญาต

Clipboard Events API และ isTrusted

  • ClipboardEvent เกิดขึ้นในเหตุการณ์ copy, cut, paste และให้วัตถุ DataTransfer ผ่านพร็อพเพอร์ตี clipboardData
  • ในเหตุการณ์ copy สามารถเขียนข้อมูลด้วย e.clipboardData.setData("text/plain", "..."), setData("text/html", "...")
  • ในเหตุการณ์ paste สามารถอ่านการแทนค่า HTML ด้วย e.clipboardData.getData("text/html")
  • clipboardData.setData("application/json", json) ไม่โยนข้อยกเว้น และใน paste handler ก็สามารถเห็นประเภท application/json กับสตริง JSON ได้
  • clipboardData เป็นอินเทอร์เฟซที่เก่ากว่า async Clipboard API มาก
    • async Clipboard API ถูกเพิ่มเข้าในสเปกในปี 2017
    • clipboardData, setData, getData ปรากฏอยู่แล้วในร่าง W3C ปี 2006
    • ร่างดังกล่าวสะท้อนฟีเจอร์ที่นำไปใช้ใน Internet Explorer เป็นอย่างมาก
    • เนื่องจาก Internet Explorer 4 เปิดตัวในปี 1997 จึงมองได้ว่าอินเทอร์เฟซ clipboardData มีอายุอย่างน้อย 26 ปี ณ เวลาที่เขียนบทความ
  • MIME type ถูกใส่เข้ามาตั้งแต่สเปกปี 2011 แต่คุณสมบัติที่อาร์กิวเมนต์ประเภทของ setData() สามารถใช้สตริงใดก็ได้ยังคงอยู่จนถึงปัจจุบัน
  • Event.isTrusted เป็นพร็อพเพอร์ตีแบบอ่านอย่างเดียวที่จะเป็น true เฉพาะเมื่อเหตุการณ์ถูกสร้างโดย user agent
    • เหตุการณ์ copy ที่ผู้ใช้กด Command+C จะมี isTrusted เป็น true
    • เหตุการณ์สังเคราะห์ที่สร้างด้วย dispatchEvent() จะมี isTrusted เป็น false
  • เหตุการณ์ copy/cut สังเคราะห์ไม่สามารถแก้ไขคลิปบอร์ดของระบบได้ และเหตุการณ์ paste สังเคราะห์ไม่สามารถเข้าถึงข้อมูลจริงในคลิปบอร์ดของระบบได้
  • ดังนั้น Clipboard Events API จึงจัดการ ประเภทใดก็ได้ แต่การโต้ตอบกับคลิปบอร์ดจริงถูกจำกัดไว้ใน handler ของเหตุการณ์ copy/paste ที่ user agent สร้างขึ้น

การเขียนประเภทกำหนดเองจากปุ่ม Copy

  • ปุ่ม Copy ในเมนูคลิกขวาของ Google Docs เขียนการแทนค่าสามแบบลงคลิปบอร์ด
    • text/plain
    • text/html
    • application/x-vnd.google-docs-document-slice-clip+wrapped
  • การแทนค่าที่สามเป็นประเภทกำหนดเองที่บรรจุข้อมูล JSON และไม่สามารถเขียนได้ด้วย async Clipboard API เพียงอย่างเดียว
  • Google Docs เรียก document.execCommand("copy") เมื่อคลิกปุ่ม Copy
  • execCommand("copy") สามารถสร้าง เหตุการณ์ copy ที่เชื่อถือได้ แบบโปรแกรมได้ เหมือนผู้ใช้สั่งคัดลอกเอง
  • ใน Safari หากต้องการให้ execCommand("copy") สร้างเหตุการณ์ copy จำเป็นต้องมี selection ที่ใช้งานอยู่
    • สามารถสร้าง selection ได้ด้วยการเพิ่ม input ที่ไม่ว่างลงใน DOM เลือกข้อความ เรียก execCommand("copy") แล้วลบออก

ความแตกต่างของเบราว์เซอร์และ OS สำหรับปุ่ม Paste

  • ปุ่ม Paste ของ Google Docs เคยกำหนดให้ติดตั้งส่วนขยายบน macOS แต่ทำงานได้บนแล็ปท็อป Windows
  • สามารถตรวจสอบการรองรับคำสั่ง paste ได้ด้วย document.queryCommandSupported("paste")
    • บน macOS Chrome และ Firefox เป็น false, Safari เป็น true
    • บน Windows Chrome และ Edge เป็น true, Firefox เป็น false
  • Safari แสดงพรอมป์ยืนยันการวาง ทำให้เห็นชัดว่าเว็บไซต์กำลังอ่านคลิปบอร์ด
  • ยังไม่ทราบเหตุผลที่ Chrome อนุญาต execCommand("paste") บน Windows แต่ไม่อนุญาตบน macOS
  • Google Docs ไม่ fallback ไปใช้ async Clipboard API เมื่อใช้ execCommand("paste") ไม่ได้
    • async Clipboard API ไม่สามารถอ่านการแทนค่า application/x-vnd.google-[...] ได้
    • การแทนค่า HTML มี ID ภายในอยู่

การคัดลอกและวางของ Figma ที่อิง HTML

  • ปุ่ม Copy ของ Figma เขียนการแทนค่าสองแบบลงคลิปบอร์ดคือ text/plain และ text/html
  • การแทนค่า HTML มี span ว่างสองตัว โดยแต่ละตัวมีแอตทริบิวต์ data-metadata, data-buffer
  • data-metadata เป็นสตริง JSON ที่เข้ารหัส base64 และเมื่อถอดรหัสจะได้ค่าต่าง ๆ เช่น fileKey, pasteID, dataType
  • data-buffer มีความยาวประมาณ 26,000 ตัวอักษรแม้คัดลอกแค่เฟรมว่าง และดูเหมือนความยาวจะเพิ่มขึ้นเชิงเส้นตามปริมาณคอนเทนต์ที่คัดลอก
  • เมื่อถอดรหัส base64 ของ data-buffer จะได้ข้อมูลไบนารีที่ขึ้นต้นด้วย fig-kiwi ซึ่งเป็น Kiwi message format
    • Kiwi เป็นฟอร์แมตที่สร้างโดย Evan Wallace ผู้ร่วมก่อตั้ง Figma และอดีต CTO
    • ใช้ในการเข้ารหัสไฟล์ .fig
  • สามารถใช้ parser ไฟล์ .fig ที่ Evan Wallace เผยแพร่ เพื่อแปลง data-buffer เป็นไฟล์ .fig แล้วตรวจสอบได้
  • Figma สร้างไฟล์ Figma ขนาดเล็ก เข้ารหัสเป็น base64 แล้วใส่ไว้ในแอตทริบิวต์ data-buffer ของ span HTML ว่าง เพื่อบันทึกลงคลิปบอร์ด

เหตุผลที่ใช้การแทนค่า HTML

  • Windows, macOS, Linux มีฟอร์แมต OS ที่ต่างกันสำหรับเขียนข้อมูลลงคลิปบอร์ด
    • Windows มี CF_HTML สำหรับ HTML
    • macOS มี NSPasteboard.PasteboardType.html
  • ข้อความธรรมดา, HTML, รูปภาพ PNG สามารถแมปเป็นฟอร์แมตมาตรฐานในหลาย OS ได้
  • ประเภทใดก็ได้อย่าง application/foo-bar ไม่ค่อยเข้ากับฟอร์แมตกลางของคลิปบอร์ด OS
  • เบราว์เซอร์ไม่เขียนการแทนค่ากำหนดเองเหล่านี้ลงในฟอร์แมตคลิปบอร์ดกลางของ OS แต่เก็บไว้ในฟอร์แมตคลิปบอร์ดกำหนดเองของแต่ละเบราว์เซอร์
    • ผลคือประเภทกำหนดเองสามารถคัดลอกและวางระหว่างแท็บของเบราว์เซอร์ได้
    • แต่คัดลอกและวางระหว่างแอปพลิเคชันไม่ได้
  • การใช้ text/html จะถูกแมปเป็นฟอร์แมตคลิปบอร์ดกลางของ OS ทำให้แอปพลิเคชันอื่นอ่านได้ง่าย
  • Figma ใช้ text/html เพื่อให้คัดลอกและวางองค์ประกอบระหว่าง figma.com ในเบราว์เซอร์กับแอป Figma แบบเนทีฟได้

วิธีจัดเก็บข้อมูลกำหนดเองในแต่ละเบราว์เซอร์

  • บน macOS หากเขียน text/plain, text/html, application/json, foo bar baz ภายในเหตุการณ์ copy แล้วตรวจสอบด้วย Pasteboard Viewer จะเห็นว่าแต่ละเบราว์เซอร์จัดเก็บไม่เหมือนกัน
  • Chrome เพิ่มรายการสี่รายการใน Pasteboard
    • public.html: การแทนค่า HTML
    • public.utf8-plain-text: การแทนค่าข้อความธรรมดา
    • org.chromium.web-custom-data: การแทนค่ากำหนดเอง
    • org.chromium.source-url: URL ของหน้าเว็บที่ทำการคัดลอก
  • Firefox ก็สร้าง public.html, public.utf8-plain-text แต่เขียนข้อมูลกำหนดเองไว้ใน org.mozilla.custom-clipdata
    • ต่างจาก Chrome ตรงที่ไม่บันทึก source URL
  • Safari ก็สร้าง public.html, public.utf8-plain-text และเขียนข้อมูลกำหนดเองไว้ใน com.apple.WebKit.custom-pasteboard-data
    • ในรายการนี้ยังเก็บรายการการแทนค่าทั้งหมดรวมถึงข้อความธรรมดาและ HTML ตลอดจน source URL ด้วย
  • Safari อนุญาตให้คัดลอกและวางประเภทกำหนดเองระหว่างแท็บเบราว์เซอร์เฉพาะเมื่อโดเมนของ source URL เหมือนกัน
    • ใน Chrome และ Firefox ไม่พบข้อจำกัดแบบเดียวกัน

Raw Clipboard Access และ Web Custom Formats

  • Raw Clipboard Access เป็น API ที่เสนอในปี 2019 โดยตั้งใจให้เว็บแอปพลิเคชันเข้าถึงการอ่านและเขียนแบบดิบของคลิปบอร์ด OS เนทีฟ
  • Raw Clipboard Access แก้ปัญหาที่เว็บแอปพลิเคชันถูกจำกัดไว้กับฟอร์แมตไม่กี่ชนิด ทำให้ Figma และ Photopea ทำงานร่วมกับฟอร์แมตรูปภาพส่วนใหญ่ได้ยาก
  • ข้อเสนอนี้ไม่ได้เดินหน้าต่อ เนื่องจาก ความกังวลด้านความปลอดภัย เช่น การรันโค้ดจากระยะไกลในแอปพลิเคชันเนทีฟ
  • Web Custom Formats เป็นข้อเสนอสำหรับใช้ประเภทกำหนดเองใน async Clipboard API และ Chromium นำไปใช้ในปี 2022
  • Web Custom Formats ใช้โดยเติม "web " นำหน้า MIME type
    • ตัวอย่าง: web application/json
    • ตอนอ่านก็ใช้ read() ของ async Clipboard API และ getType("web application/json")
  • Web Custom Formats เขียนข้อมูลสองประเภทลงในคลิปบอร์ด OS เนทีฟ
    • การแมประหว่าง data type กับชื่อรายการคลิปบอร์ด
    • รายการคลิปบอร์ดของแต่ละ data type
  • บน macOS การแมปถูกเก็บไว้ใน org.w3.web-custom-format.map
    • ตัวอย่าง: "application/json": "org.w3.web-custom-format.type-0"
  • รายการ org.w3.web-custom-format.type-[index] มี ข้อมูลดิบ ของ Blob อยู่
  • วิธีนี้หลีกเลี่ยงปัญหาความปลอดภัยของ Raw Clipboard Access โดยไม่ให้เว็บแอปพลิเคชันเขียนข้อมูลดิบโดยตรงลงในฟอร์แมตคลิปบอร์ด OS ที่ต้องการ
  • แต่เป้าหมายไม่ใช่การทำงานร่วมกับแอปพลิเคชันเนทีฟแบบ legacy โดยไม่ต้องอัปเดต
    • แอปพลิเคชันเนทีฟต้องอัปเดตให้อ่านฟอร์แมต Web Custom Formats ได้
  • Web Custom Formats ใช้ได้ในเบราว์เซอร์ฐาน Chromium ตั้งแต่ปี 2022 แต่เบราว์เซอร์อื่นยังไม่ได้ implement

ตัวเลือก unsanitized

  • เมื่ออ่านคลิปบอร์ดด้วย async Clipboard API เบราว์เซอร์อาจ sanitize ข้อมูลได้
    • อาจลบแท็ก script ที่อาจเป็นอันตรายออกจาก HTML
    • อาจเข้ารหัสรูปภาพ PNG ใหม่เพื่อหลีกเลี่ยงการโจมตีแบบ zip bomb
  • ตัวเลือก unsanitized ของ read() ทำให้สามารถร้องขอข้อมูลที่ไม่ถูก sanitize ได้
  • ปัจจุบันตัวเลือกนี้รองรับเฉพาะเบราว์เซอร์ฐาน Chromium
    • เพิ่มเข้ามาเมื่อปลายปี 2023
    • เบราว์เซอร์อื่นอาจรองรับในอนาคต แต่มีฟีดแบ็กว่า Safari ดูมีความเป็นไปได้น้อย

ทางเลือกที่เป็นจริงในตอนนี้

  • ตอนนี้ยังไม่มีวิธีเขียนประเภทคลิปบอร์ดกำหนดเองที่ทำงานได้ดีในทุกเบราว์เซอร์
  • วิธีใส่สตริง base64 ไว้ในการแทนค่า HTML แบบ Figma นั้นค่อนข้างหยาบ แต่สามารถอ้อมข้อจำกัดหลายอย่างของ Clipboard API เพื่อส่งข้อมูลกำหนดเองผ่านคลิปบอร์ดได้
  • Web Custom Formats ชี้แนวทางให้ใช้ประเภทกำหนดเองได้อย่างปลอดภัยและใช้งานได้จริงมากขึ้น แต่ยังต้องมีการ implement ครอบคลุมเบราว์เซอร์หลัก ๆ

1 ความคิดเห็น

 
GN⁺ 2024-09-02
ความคิดเห็นบน Hacker News
  • เป็นบทความที่ดี เข้าใจแล้วว่าทำไมการ วางแบบคง/ไม่คงรูปแบบ ใน Google Docs บน Firefox ของ Mac ถึงทำงานได้ไม่ค่อยดี
    น่าสนใจที่ Google ใช้ API ที่เลิกใช้แล้ว เพื่อพยายามใส่รูปแบบเนื้อหาของตัวเองลงในคลิปบอร์ด

    • สักวันหนึ่งเราอาจต้องประกาศให้ เว็บของ Google เป็นฟอร์กอย่างเป็นทางการของเว็บก็ได้
  • วิธีที่น่าเชื่อถือที่สุดในการส่งข้อมูลแอปแบบกำหนดเองไปยังคลิปบอร์ด ดูเหมือนจะเป็น การฝังข้อมูลไว้ใน HTML แบบที่ Figma ทำ
    ข้อดีอีกอย่างคือ เมื่อแอปที่รับไม่รองรับ ก็ยังสามารถกำหนดพฤติกรรมตอนล้มเหลวเป็นข้อความ HTML ได้

  • WordPress ก็แตะปัญหานี้เหมือนกัน ถ้าตัดและวางหลายย่อหน้าในโหมดตัวแก้ไข อาจพังหนักได้
    น่าจะเพราะย่อหน้าเหล่านั้นจริง ๆ แล้วเป็นพื้นที่ DIV คนละส่วนกัน และอยู่ภายใต้ลอจิกควบคุมกับเอฟเฟกต์ที่ต่างกัน
    อีกอย่างคือเครื่องมักยกระดับสิ่งที่ผมคิดว่าเป็น ASCII ให้เป็น UTF-8 หรือ ISO Latin-1 แล้วทำพฤติกรรมแบบ Clippy ว่า “ฉันเปลี่ยนเครื่องหมายอัญประกาศให้ดูดีขึ้นแล้ว ไว้ค่อยขอบคุณฉันทีหลังนะ” ซึ่งผมไม่เคยต้องการ ถ้าผมอยากได้ `this' ผมก็คงไม่พิมพ์ 'this' หรอก

  • สมัยก่อนตอนเป็นนักเรียน JavaScript ยังอ่านคลิปบอร์ดได้โดยไม่ต้องขอความยินยอมจากผู้ใช้ และผมเคยทำเว็บไซต์ชื่อ getpasted
    มันเป็นเว็บที่วางเนื้อหาจากคลิปบอร์ดโดยอัตโนมัติแล้วอัปโหลดไปยังฐานข้อมูลสาธารณะ แน่นอนว่ามีคนไม่ชอบ
    แต่ในฐานะโปรเจกต์ที่บอกให้รู้ว่าคลิปบอร์ดอ่านได้ตลอดเวลา ก็ถือว่าใช้ได้

    • ผู้คนน่าจะตกใจถ้ารู้ว่าใน Windows 11 ตามค่าเริ่มต้น ข้อมูลคลิปบอร์ดทั้งหมดจะถูกส่งไปยังเซิร์ฟเวอร์ของ Microsoft ผ่าน Clipboard History / Cloud Clipboard
  • Sindre Sorhus ผู้สร้าง Pasteboard Manager[0] ยังเป็นคนทำไลบรารี Shortcuts ชื่อ Actions[1] และแอป iPhone/Mac หลายตัวด้วย ไม่รู้ว่าเขาอยู่ใน HN หรือเปล่า
    0. https://apps.apple.com/us/app/pasteboard-viewer/id1499215709

    1. https://apps.apple.com/us/app/actions/id1586435171
    • ชื่อนั้นคุ้น ๆ เขายังปล่อย แพ็กเกจ NPM ดี ๆ ไว้ค่อนข้างเยอะด้วย
      https://www.npmjs.com/~sindresorhus
  • การผสมกันของขอบเขตและความลึกดีเป็นพิเศษ ครอบคลุมถึงความแตกต่างตามระบบและเบราว์เซอร์ ขณะเดียวกันก็อธิบายทั้ง กับดักและวิธีเลี่ยง ไปด้วย

    • ผมพยายามลดรายละเอียดที่มากเกินไปกับประเด็นปลีกย่อยในบทความมาตลอด บทความค่อนข้างยาว แต่ครั้งนี้ดีใจที่เหมือนจะบาลานซ์ได้ดี
  • เกี่ยวกับเรื่องการดึงข้อมูล “ส่วนตัว” จากเบราว์เซอร์ เว็บแอปของธนาคารผมตอนล็อกอินแสดง ชื่อโฮสต์ ของผมคือ aluminium แล้วขอการยืนยันตัวตนสองขั้นตอน
    ไม่รู้ว่ารู้ได้ยังไง ถ้าเข้าจากมือถือ มันยังเห็น SMS ที่มีโค้ดยืนยันสองขั้นตอนและวางให้อัตโนมัติได้ด้วย เป็นสภาพแวดล้อม Pixel+Chrome หรือ Linux+Chrome

    • โค้ดยืนยันสองขั้นตอนน่าจะเป็น ฟีเจอร์ของ Chrome มากกว่า Safari ก็แสดงโค้ดยืนยันจาก SMS เป็นคำแนะนำเติมอัตโนมัติ และ Chrome บน Android ก็น่าจะกรอกให้อัตโนมัติได้เลย
      ส่วนชื่อโฮสต์ยากกว่า ควรตรวจดูจริง ๆ ว่าเคยให้ข้อมูลนั้นแก่ธนาคารในรูปแบบอย่างชื่อผู้ใช้หรือเปล่า อยากรู้ด้วยว่าเป็นธนาคารไหน
  • บทความนี้แสดงให้เห็นชัดว่าทำไม เว็บแอปถึงไม่มีทางดีเท่าแอปเนทีฟได้
    เว็บแอปเป็นโค้ดที่ “ไม่น่าเชื่อถือ” เสมอ ดังนั้นการเข้าถึงทรัพยากรของเครื่องโลคัลจึงถูกจำกัดแบบตามอำเภอใจและประดิษฐ์ขึ้น

    • การไม่เชื่อถือโดยค่าเริ่มต้นคือ ฟีเจอร์ หากผู้ใช้ไม่ใช่ผู้เชี่ยวชาญความปลอดภัยที่ผ่านการฝึกมาอย่างสูงและระแวดระวังตลอดเวลา ก็ยากที่จะปกป้องตัวเอง
      ตอนนี้ไม่ใช่ยุคอินเทอร์เน็ตช่วงแรก ๆ ที่มีแต่ script kiddie อาละวาด และไม่ใช่พื้นที่ที่มีแต่พวกกี๊กใช้อีกแล้ว นี่คือยุคที่องค์กรที่เชื่อมโยงกับรัฐบาลโจมตีแรนซัมแวร์ต่อโครงสร้างพื้นฐานสำคัญเพื่อหาเงินสนับสนุนโครงการนิวเคลียร์ การเข้าถึงทรัพยากรใด ๆ บนเครื่องโลคัลได้นั่นแหละที่ทำให้เรื่องแบบนั้นเกิดขึ้นได้
      เว็บแอปในเบราว์เซอร์สมัยใหม่ย่อมอยู่ในแซนด์บ็อกซ์ที่แข็งแรงกว่า แต่ในระบบปฏิบัติการสมัยใหม่ แอปเนทีฟก็ถูกปฏิบัติเป็นสิ่งที่ไม่น่าเชื่อถือเช่นกัน เมื่อรันแอปใหม่ จะมีไดอะล็อกยืนยันก่อนเข้าถึงสิ่งใดนอกไดเรกทอรีข้อมูลแอปที่ถูกแยกไว้
  • แต่ก็สงสัยว่า ส่วนขยาย Chrome ตัวไหนกันที่ทำให้มีคำเตือนเด้งขึ้นบน MacBook