นี่คือวิธี:
[ พฤศจิกายน 2020 ปรับปรุง: การแก้ปัญหานี้ต้องอาศัยความสามารถในการตั้งค่า document.domain
ความสามารถในการดำเนินการดังกล่าวได้ถูกยกเลิกไปแล้วอย่างน่าเสียดาย ]
สำหรับการแชร์ระหว่างโดเมนย่อยของ superdomain ที่กำหนด (เช่น example.com) มีเทคนิคที่คุณสามารถใช้ในสถานการณ์นั้นได้ มันสามารถนำไปใช้localStorage
, IndexedDB
, SharedWorker
, BroadcastChannel
ฯลฯ ซึ่งทั้งหมดนี้มีฟังก์ชันการทำงานร่วมกันระหว่างหน้าเดียวกันกำเนิด แต่ด้วยเหตุผลบางอย่างไม่เคารพการปรับเปลี่ยนใด ๆ เพื่อdocument.domain
ที่จะให้พวกเขาใช้ superdomain เป็นแหล่งกำเนิดของพวกเขาโดยตรง
(1) เลือกโดเมน "หลัก" หนึ่งโดเมนเพื่อให้ข้อมูลเป็นของ: เช่นhttps://example.comหรือhttps://www.example.comจะเก็บข้อมูล localStorage ของคุณ สมมติว่าคุณเลือกhttps://example.com
(2) ใช้ localStorage ตามปกติสำหรับเพจของโดเมนที่เลือกนั้น
(3) ในทุกhttps://www.example.comหน้า (คนอื่น ๆโดเมน) document.domain = "example.com";
การใช้งานจาวาสคริปต์เพื่อชุด แล้วยังสร้างที่ซ่อนอยู่<iframe>
และนำทางไปยังบางหน้าได้รับการแต่งตั้งhttps://example.comโดเมน ( มันไม่สำคัญว่าสิ่งที่หน้าตราบใดที่คุณสามารถแทรกตัวอย่างน้อยมากของ JavaScript บนมี. ถ้าคุณ' กำลังสร้างไซต์เพียงแค่สร้างหน้าว่างโดยเฉพาะเพื่อจุดประสงค์นี้หากคุณกำลังเขียนส่วนขยายหรือผู้ใช้แบบ Greasemonkey และไม่มีการควบคุมหน้าใด ๆ บนexample.comเซิร์ฟเวอร์เพียงเลือกหน้าที่มีน้ำหนักเบาที่สุดที่คุณสามารถค้นหาและแทรกสคริปต์ของคุณลงในนั้น หน้า "ไม่พบ" บางประเภทก็น่าจะใช้ได้)
(4) สคริปต์บนเพจ iframe ที่ซ่อนอยู่ต้องการเพียง (a) set document.domain = "example.com";
และ (b) แจ้งหน้าต่างหลักเมื่อเสร็จสิ้น หลังจากนั้นหน้าต่างหลักสามารถเข้าถึงหน้าต่าง iframe และวัตถุทั้งหมดได้โดยไม่มีข้อ จำกัด ! ดังนั้นหน้า iframe ขั้นต่ำจึงเป็นดังนี้:
<!doctype html>
<html>
<head>
<script>
document.domain = "example.com";
window.parent.iframeReady();
</script>
</head>
<body></body>
</html>
หากเขียน userscript คุณอาจไม่ต้องการเพิ่มฟังก์ชันที่เข้าถึงได้จากภายนอกเช่นในiframeReady()
ของคุณunsafeWindow
ดังนั้นวิธีที่ดีกว่าในการแจ้ง userscript ของหน้าต่างหลักอาจเป็นการใช้เหตุการณ์ที่กำหนดเอง:
window.parent.dispatchEvent(new CustomEvent("iframeReady"));
ซึ่งคุณจะตรวจพบได้โดยการเพิ่ม Listener สำหรับเหตุการณ์ "iframeReady" ที่กำหนดเองในหน้าต่างหน้าหลัก
(หมายเหตุ: คุณต้องตั้งค่า document.domain = "example.com" แม้ว่าโดเมนของ iframe จะเป็นexample.comอยู่แล้วก็ตาม: การกำหนดค่าให้กับ document.domain โดยปริยายจะตั้งค่าพอร์ตของต้นทางเป็นค่าว่างและพอร์ตทั้งสองต้องตรงกันสำหรับ iframe และพาเรนต์ถือเป็นแหล่งกำเนิดเดียวกันดูหมายเหตุที่นี่: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin )
(5) เมื่อซ่อน iframe ได้แจ้งหน้าต่างของแม่ว่ามันเป็นพร้อมสคริปต์ในหน้าต่างหลักก็สามารถใช้iframe.contentWindow.localStorage
, iframe.contentWindow.indexedDB
, iframe.contentWindow.BroadcastChannel
, iframe.contentWindow.SharedWorker
แทนwindow.localStorage
, window.indexedDB
ฯลฯ ... และวัตถุทั้งหมดเหล่านี้จะถูกกำหนดขอบเขตที่จะได้รับการแต่งตั้งhttps: // example.com ที่มา - ดังนั้นจะมีต้นกำเนิดร่วมกันนี้สำหรับทุกเพจของคุณ!
ส่วนที่น่าอึดอัดที่สุดของเทคนิคนี้คือคุณต้องรอให้ iframe โหลดก่อนที่จะดำเนินการต่อ ดังนั้นคุณจึงไม่สามารถเริ่มใช้ localStorage ในตัวจัดการ DOMContentLoaded ของคุณได้ นอกจากนี้คุณอาจต้องการเพิ่มการจัดการข้อผิดพลาดเพื่อตรวจสอบว่า iframe ที่ซ่อนไม่สามารถโหลดได้อย่างถูกต้องหรือไม่
เห็นได้ชัดว่าคุณควรตรวจสอบให้แน่ใจว่า iframe ที่ซ่อนอยู่ไม่ได้ถูกลบออกหรือถูกสำรวจตลอดช่วงอายุการใช้งานของเพจของคุณ ... OTOH ฉันไม่รู้ว่าผลลัพธ์ของมันจะเป็นอย่างไร แต่สิ่งที่ไม่ดีจะเกิดขึ้น
และข้อแม้: การตั้งค่า / การเปลี่ยนแปลงdocument.domain
สามารถบล็อกได้โดยใช้Feature-Policy
ส่วนหัวซึ่งในกรณีนี้เทคนิคนี้จะไม่สามารถใช้งานได้ตามที่อธิบายไว้
อย่างไรก็ตามมีการกำหนดลักษณะทั่วไปที่ซับซ้อนมากขึ้นอย่างมีนัยสำคัญของเทคนิคนี้ซึ่งไม่สามารถบล็อกได้Feature-Policy
และยังอนุญาตให้โดเมนที่ไม่เกี่ยวข้องทั้งหมดแชร์ข้อมูลการสื่อสารและคนงานที่ใช้ร่วมกันได้ (เช่นไม่ใช่แค่โดเมนย่อยนอกโดเมนหลักทั่วไป) @ Mayank Jain อธิบายไว้แล้วในคำตอบของพวกเขากล่าวคือ:
แนวคิดทั่วไปก็คือคุณสร้าง iframe ที่ซ่อนอยู่เพื่อระบุต้นทางที่ถูกต้องสำหรับการเข้าถึง แต่แทนที่จะแล้วก็โลภคุณสมบัติหน้าต่าง iframe โดยตรงคุณใช้สคริปต์ภายใน iframe ที่จะทำทั้งหมดของการทำงานและให้คุณติดต่อสื่อสารระหว่าง iframe และหน้าต่างหลักของคุณเป็นเพียงการใช้และpostMessage()
addEventListener("message",...)
ใช้งานได้เนื่องจากpostMessage()
สามารถใช้งานได้แม้กระทั่งระหว่างหน้าต่างที่มาจากแหล่งอื่น แต่ก็ซับซ้อนกว่าอย่างมากเนื่องจากคุณต้องส่งทุกอย่างผ่านโครงสร้างพื้นฐานการส่งข้อความบางประเภทที่คุณสร้างขึ้นระหว่าง iframe และหน้าต่างหลักแทนที่จะใช้ localStorage, IndexedDB และอื่น ๆ API โดยตรงในโค้ดของหน้าต่างหลักของคุณ