summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/measure-memory/resources/common.js
blob: 4332c6e79c10f4ccd166a77771c159ce7c1e9d7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
const ORIGINS = {
  'same-origin': get_host_info().HTTPS_ORIGIN,
  'cross-origin': get_host_info().HTTPS_REMOTE_ORIGIN,
  'cross-site': get_host_info().HTTPS_NOTSAMESITE_ORIGIN,
}

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}/measure-memory/resources/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();
  }
})();

function getMainWindow() {
  let main = window;
  while (true) {
    if (main === main.parent) {
      if (!main.opener) {
        break;
      } else {
        main = main.opener;
      }
    } else {
      main = main.parent;
    }
  }
  return main;
}

function isSameOrigin(other) {
  try {
    other.descendants;
  } catch (e) {
    // Cross-origin iframe that cannot access the main frame.
    return false;
  }
  return !!other.descendants;
}

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;
}

// Constructs iframes based on their descriptoin.
async function build(children) {
  window.descendants = {iframes: {}, windows: {}};
  await Promise.all(children.map(buildChild));
  const result = window.descendants;
  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));
    if (!params.id.startsWith('same-origin')) {
      // Cross-origin windows gets their own browsing context groups with COOP.
      // The postMessage calls before would not work for them, so we do not
      // wait for them to load.
      return;
    }
  } 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);
  if (!params.window_open) {
    const main = getMainWindow();
    if (isSameOrigin(main)) {
      main.descendants.iframes[params.id] = child;
    }
  }
}

// This function runs within an iframe.
// It gets the children descriptions from the parent and constructs them.
async function setupChild() {
  const id = getId();
  const main = getMainWindow();
  if (isSameOrigin(main)) {
    main.descendants.windows[id] = window;
  }
  document.getElementById('title').textContent = id;
  getParent().postMessage({id : id, payload: 'ready'}, '*');
  const children = await waitForMessage('parent');
  if (children) {
    await Promise.all(children.map(buildChild));
  }
  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) {
    resolve_promise(message.data);
  }
  worker.postMessage({bytes});
  return promise;
}