tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }