1/* iframe-tracker.js â canvas iframe sister-app URL tracking + cast-save (Phase 4 / 0.9.0; cast 0.x) 2 * 3 * A trusted sister app (piratesbargain.com, 2plot.ai, cast.2plot.net, â¦) posts to the canvas. Two 4 * envelopes, both accepted ONLY when event.origin is on the server-provided allow-list 5 * (window.__trustedEmbedOrigins, mirrored from the `trusted-embed-origins` store by a clientside 6 * callback) and matched to the iframe that sent them by contentWindow: 7 * 8 * 1) {type:'canvas-iframe-url', url: location.href} â a navigation beacon. We push {id, url, ts} 9 * into `iframe-url-sync`; a server callback persists the tile's `last_url` so the iframe reopens 10 * its last page across save/reload. 11 * 2) {type:'cast-save', filmstrip: <manifest>} â an embedded cast.2plot.net session baked a run and 12 * the user clicked "Save to canvas". We push {filmstrip, source_id, ts} into `cast-save-sync`; a 13 * server callback (state_sync.sync_cast_save) validates the manifest (URL frames only) and spawns 14 * a DashFilmstrip tile next to the source session. The manifest is a few KB of CDN URLs â the 15 * canvas never touches image bytes. 16 * 3) {type:'canvas-iframe-thumb', thumb_url: <https image URL>} â a self-reported thumbnail: the 17 * sister app hands us a pre-rendered screenshot of itself. We push {id, thumb_url, ts} into 18 * `iframe-thumb-sync`; a server callback persists it and the save-time rehost 19 * (lib/iframe_thumbs) downloads it into OUR R2 in place of a cast Chromium capture. URLs only â 20 * the bytes move server-side at save time. 21 * 22 * SECURITY: fails CLOSED â an empty/undefined allow-list rejects every message; arbitrary external sites 23 * (which we don't control and whose URL the browser SOP hides anyway) are never tracked. This is exactly 24 * the mechanism that lifts that wall for the sister apps we DO control. Origin is verified BEFORE we read 25 * any field of event.data, and each message is only ever associated with the frame that actually sent it. 26 */ 27(function () { 28 'use strict'; 29 if (window.__iframeTrackerBound) return; 30 window.__iframeTrackerBound = true; 31 window.__trustedEmbedOrigins = window.__trustedEmbedOrigins || []; 32 33 function parseCompId(frame) { 34 // The canvas iframe id is the stringified pattern {"type":"canvas-iframe","index":"<comp_id>"}. 35 try { 36 var parsed = JSON.parse(frame.id); 37 if (parsed && parsed.type === 'canvas-iframe' && parsed.index) return parsed.index; 38 } catch (e) { /* not a canvas iframe */ } 39 return null; 40 } 41 42 function sourceCompId(source) { 43 // The comp_id of the canvas iframe tile whose contentWindow sent this message (or null). 44 var frames = document.querySelectorAll('iframe'); 45 for (var i = 0; i < frames.length; i++) { 46 if (frames[i].contentWindow === source) return parseCompId(frames[i]); 47 } 48 return null; 49 } 50 51 window.addEventListener('message', function (event) { 52 // 1) Origin gate FIRST â fail closed. 53 var allow = window.__trustedEmbedOrigins; 54 if (!Array.isArray(allow) || allow.indexOf(event.origin) === -1) return; 55 56 var d = event.data; 57 if (!d || typeof d !== 'object') return; 58 if (!(window.dash_clientside && window.dash_clientside.set_props)) return; 59 60 // 2a) Navigation beacon â persist the iframe's last URL (matched to the frame that sent it). 61 if (d.type === 'canvas-iframe-url' && typeof d.url === 'string') { 62 var compId = sourceCompId(event.source); 63 if (compId) { 64 window.dash_clientside.set_props('iframe-url-sync', { 65 data: { id: compId, url: d.url, ts: Date.now() } 66 }); 67 } 68 return; 69 } 70 71 // 2c) Self-reported thumbnail â the sister app hands us a pre-rendered screenshot URL of 72 // itself. Server-side validation + the actual byte transfer happen at save time 73 // (state_sync.sync_iframe_thumb â lib/iframe_thumbs rehost). 74 if (d.type === 'canvas-iframe-thumb' && typeof d.thumb_url === 'string') { 75 var tid = sourceCompId(event.source); 76 if (tid) { 77 window.dash_clientside.set_props('iframe-thumb-sync', { 78 data: { id: tid, thumb_url: d.thumb_url, ts: Date.now() } 79 }); 80 } 81 return; 82 } 83 84 // 2b) cast-save â an embedded cast.2plot.net session baked a run. Hand the manifest to the 85 // server (URLs only; validated server-side) along with the source session tile's id so the 86 // new filmstrip tile can be placed next to it. 87 if (d.type === 'cast-save' && d.filmstrip && typeof d.filmstrip === 'object') { 88 window.dash_clientside.set_props('cast-save-sync', { 89 data: { filmstrip: d.filmstrip, source_id: sourceCompId(event.source), ts: Date.now() } 90 }); 91 return; 92 } 93 }, false); 94 95 console.log('ð iframe URL tracker + cast-save listener bound (trusted-embed sister-app integration)'); 96})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.