common.js (4719B)
1 const ORIGINS = { 2 'same-origin': get_host_info().HTTPS_ORIGIN, 3 'cross-origin': get_host_info().HTTPS_REMOTE_ORIGIN, 4 'cross-site': get_host_info().HTTPS_NOTSAMESITE_ORIGIN, 5 } 6 7 function url(params) { 8 let origin = null; 9 for (const key of Object.keys(ORIGINS)) { 10 if (params.id.startsWith(key)) { 11 origin = ORIGINS[key]; 12 } 13 } 14 const child = params.window_open ? 'window' : 'iframe'; 15 let file = `measure-memory/resources/${child}.sub.html`; 16 if (params.redirect) { 17 file = `measure-memory/resources/${child}.redirect.sub.html`; 18 } 19 let url = `${origin}/${file}?id=${params.id}`; 20 if (params.redirect === 'server') { 21 url = (`${origin}/measure-memory/resources/redirect.py?` + 22 `location=${encodeURIComponent(url)}`); 23 } 24 return url; 25 } 26 27 // A simple multiplexor of messages based on iframe ids. 28 let waitForMessage = (function () { 29 class Inbox { 30 constructor() { 31 this.queue = []; 32 this.resolve = null; 33 } 34 push(value) { 35 if (this.resolve) { 36 this.resolve(value); 37 this.resolve = null; 38 } else { 39 this.queue.push(value); 40 } 41 } 42 pop() { 43 let promise = new Promise(resolve => this.resolve = resolve); 44 if (this.queue.length > 0) { 45 this.resolve(this.queue.shift()); 46 this.resolve = null; 47 } 48 return promise; 49 } 50 } 51 const inbox = {}; 52 53 window.onmessage = function (message) { 54 const id = message.data.id; 55 const payload = message.data.payload; 56 inbox[id] = inbox[id] || new Inbox(); 57 inbox[id].push(payload); 58 } 59 return function (id) { 60 inbox[id] = inbox[id] || new Inbox(); 61 return inbox[id].pop(); 62 } 63 })(); 64 65 function getMainWindow() { 66 let main = window; 67 while (true) { 68 if (main === main.parent) { 69 if (!main.opener) { 70 break; 71 } else { 72 main = main.opener; 73 } 74 } else { 75 main = main.parent; 76 } 77 } 78 return main; 79 } 80 81 function isSameOrigin(other) { 82 try { 83 other.descendants; 84 } catch (e) { 85 // Cross-origin iframe that cannot access the main frame. 86 return false; 87 } 88 return !!other.descendants; 89 } 90 91 function getId() { 92 const params = new URLSearchParams(document.location.search); 93 return params.get('id'); 94 } 95 96 function getParent() { 97 if (window.parent == window && window.opener) { 98 return window.opener; 99 } 100 return window.parent; 101 } 102 103 // Constructs iframes based on their descriptoin. 104 async function build(children) { 105 window.descendants = {iframes: {}, windows: {}}; 106 await Promise.all(children.map(buildChild)); 107 const result = window.descendants; 108 return result; 109 } 110 111 async function buildChild(params) { 112 let child = null; 113 function target() { 114 return params.window_open ? child : child.contentWindow; 115 } 116 if (params.window_open) { 117 child = window.open(url(params)); 118 if (!params.id.startsWith('same-origin')) { 119 // Cross-origin windows gets their own browsing context groups with COOP. 120 // The postMessage calls before would not work for them, so we do not 121 // wait for them to load. 122 return; 123 } 124 } else { 125 child = document.createElement('iframe'); 126 child.src = url(params); 127 child.id = params.id; 128 document.body.appendChild(child); 129 } 130 const ready = await waitForMessage(params.id); 131 target().postMessage({id: 'parent', payload: params.children}, '*'); 132 const done = await waitForMessage(params.id); 133 if (!params.window_open) { 134 const main = getMainWindow(); 135 if (isSameOrigin(main)) { 136 main.descendants.iframes[params.id] = child; 137 } 138 } 139 } 140 141 // This function runs within an iframe. 142 // It gets the children descriptions from the parent and constructs them. 143 async function setupChild() { 144 const id = getId(); 145 const main = getMainWindow(); 146 if (isSameOrigin(main)) { 147 main.descendants.windows[id] = window; 148 } 149 document.getElementById('title').textContent = id; 150 getParent().postMessage({id : id, payload: 'ready'}, '*'); 151 const children = await waitForMessage('parent'); 152 if (children) { 153 await Promise.all(children.map(buildChild)); 154 } 155 getParent().postMessage({id: id, payload: 'done'}, '*'); 156 } 157 158 function sameOriginContexts(children) { 159 const result = []; 160 for (const [id, child] of Object.entries(children)) { 161 if (id.includes('same-origin')) { 162 result.push(child.contentWindow 163 ? child.contentWindow.performance : child.performance); 164 } 165 } 166 return result; 167 } 168 169 async function createWorker(bytes) { 170 const worker = new Worker('resources/worker.js'); 171 let resolve_promise; 172 const promise = new Promise(resolve => resolve_promise = resolve); 173 worker.onmessage = function (message) { 174 resolve_promise(message.data); 175 } 176 worker.postMessage({bytes}); 177 return promise; 178 }