blob: 3b246719799d2f963bc5cb4cd12500bba62ecb08 [file] [edit]
const ORIGINS = {
'same-origin': get_host_info().HTTP_ORIGIN,
'cross-origin': get_host_info().HTTP_REMOTE_ORIGIN,
'cross-site': get_host_info().HTTP_NOTSAMESITE_ORIGIN,
}
function checkMeasureMemoryBreakdown(breakdown, options, required) {
const allowed = new Set(options.allowed);
assert_own_property(breakdown, 'bytes');
assert_greater_than_equal(breakdown.bytes, 0);
assert_own_property(breakdown, 'userAgentSpecificTypes');
for (const userAgentSpecificType of breakdown.userAgentSpecificTypes) {
assert_equals(typeof userAgentSpecificType, 'string');
}
assert_own_property(breakdown, 'attribution');
for (const attribution of breakdown.attribution) {
assert_equals(typeof attribution, 'string');
assert_true(
allowed.has(attribution),
`${attribution} must be in ${JSON.stringify(options.allowed)}`);
if (required.has(attribution)) {
required.delete(attribution);
}
}
}
function checkMeasureMemory(result, options) {
assert_own_property(result, 'bytes');
assert_own_property(result, 'breakdown');
const required = new Set(options.required);
let bytes = 0;
for (let breakdown of result.breakdown) {
checkMeasureMemoryBreakdown(breakdown, options, required);
bytes += breakdown.bytes;
}
assert_equals(bytes, result.bytes);
assert_equals(required.size, 0, JSON.stringify(result.breakdown) +
' does not include ' + JSON.stringify(required.values()));
}
function url(params) {
let origin = null;
for (const key of Object.keys(ORIGINS)) {
if (params.id.startsWith(key)) {
origin = ORIGINS[key];
}
}
const child = params.window_open ? 'window' : 'iframe';
let file = `measure-memory/resources/${child}.sub.html`;
if (params.redirect) {
file = `measure-memory/resources/${child}.redirect.sub.html`;
}
let url = `${origin}/${file}?id=${params.id}`;
if (params.redirect === 'server') {
url = `${origin}/common/redirect.py?location=${encodeURIComponent(url)}`;
}
return url;
}
// A simple multiplexor of messages based on iframe ids.
let waitForMessage = (function () {
class Inbox {
constructor() {
this.queue = [];
this.resolve = null;
}
push(value) {
if (this.resolve) {
this.resolve(value);
this.resolve = null;
} else {
this.queue.push(value);
}
}
pop() {
let promise = new Promise(resolve => this.resolve = resolve);
if (this.queue.length > 0) {
this.resolve(this.queue.shift());
this.resolve = null;
}
return promise;
}
}
const inbox = {};
window.onmessage = function (message) {
const id = message.data.id;
const payload = message.data.payload;
inbox[id] = inbox[id] || new Inbox();
inbox[id].push(payload);
}
return function (id) {
inbox[id] = inbox[id] || new Inbox();
return inbox[id].pop();
}
})();
// Constructs iframes based on their descriptoin.
async function build(children) {
window.accessible_children = {iframes: {}, windows: {}};
await Promise.all(children.map(buildChild));
const result = window.accessible_children;
delete window.accessible_children;
return result;
}
async function buildChild(params) {
let child = null;
function target() {
return params.window_open ? child : child.contentWindow;
}
if (params.window_open) {
child = window.open(url(params));
} else {
child = document.createElement('iframe');
child.src = url(params);
child.id = params.id;
document.body.appendChild(child);
}
const ready = await waitForMessage(params.id);
target().postMessage({id: 'parent', payload: params.children}, '*');
const done = await waitForMessage(params.id);
let main = window;
while (true) {
if (main === main.parent) {
if (!main.opener) {
break;
} else {
main = main.opener;
}
} else {
main = main.parent;
}
}
try {
main.accessible_children;
} catch (e) {
// Cross-origin iframe that cannot access the main frame.
return;
}
if (params.window_open) {
main.accessible_children.windows[params.id] = child;
} else {
main.accessible_children.iframes[params.id] = child;
}
}
function getId() {
const params = new URLSearchParams(document.location.search);
return params.get('id');
}
function getParent() {
if (window.parent == window && window.opener) {
return window.opener;
}
return window.parent;
}
// This function runs within an iframe.
// It gets the children descriptions from the parent and constructs them.
async function setupChild() {
const id = getId();
document.getElementById('title').textContent = id;
getParent().postMessage({id : id, payload: 'ready'}, '*');
const children = await waitForMessage('parent');
if (children) {
await build(children);
}
getParent().postMessage({id: id, payload: 'done'}, '*');
}
function sameOriginContexts(children) {
const result = [];
for (const [id, child] of Object.entries(children)) {
if (id.includes('same-origin')) {
result.push(child.contentWindow
? child.contentWindow.performance : child.performance);
}
}
return result;
}
async function createWorker(bytes) {
const worker = new Worker('resources/worker.js');
let resolve_promise;
const promise = new Promise(resolve => resolve_promise = resolve);
worker.onmessage = function (message) {
assert_equals(message.data, 'ready');
resolve_promise();
}
worker.postMessage({bytes});
return promise;
}