tor-browser

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

local-url-inherit-controller-frame.html (4224B)


      1 <!DOCTYPE html>
      2 <html>
      3 <script>
      4 
      5 const fetchURL = new URL('sample.js', window.location).href;
      6 
      7 const frameControllerText =
      8 `<script>
      9  let t = null;
     10  try {
     11    if (navigator.serviceWorker.controller) {
     12      t = navigator.serviceWorker.controller.scriptURL;
     13    }
     14  } catch (e) {
     15    t = e.message;
     16  } finally {
     17    parent.postMessage({ data: t }, '*');
     18  }
     19 </` + `script>`;
     20 
     21 const frameFetchText =
     22 `<script>
     23  fetch('${fetchURL}', { mode: 'no-cors' }).then(response => {
     24    return response.text();
     25  }).then(text => {
     26    parent.postMessage({ data: text }, '*');
     27  }).catch(e => {
     28    parent.postMessage({ data: e.message }, '*');
     29  });
     30 </` + `script>`;
     31 
     32 const workerControllerText =
     33 `let t = navigator.serviceWorker.controller
     34       ? navigator.serviceWorker.controller.scriptURL
     35       : null;
     36 self.postMessage(t);`;
     37 
     38 const workerFetchText =
     39 `fetch('${fetchURL}', { mode: 'no-cors' }).then(response => {
     40  return response.text();
     41 }).then(text => {
     42  self.postMessage(text);
     43 }).catch(e => {
     44  self.postMessage(e.message);
     45 });`;
     46 
     47 const sharedWorkerControllerText =
     48 `const ports = [];
     49 self.onconnect = evt => {
     50  const port = evt.ports[0];
     51  ports.push(port);
     52  const t = navigator.serviceWorker.controller
     53        ? navigator.serviceWorker.controller.scriptURL
     54        : null;
     55  port.postMessage(t);
     56 };
     57 self.onerror = msg => {
     58  ports.forEach(port => {port.postMessage(msg);});
     59 };`;
     60 
     61 const sharedWorkerFetchText =
     62 `self.onconnect = evt => {
     63  const port = evt.ports[0];
     64  fetch('${fetchURL}', { mode: 'no-cors' }).then(response => {
     65    return response.text();
     66  }).then(text => {
     67    port.postMessage(text);
     68  }).catch(e => {
     69    port.postMessage(e.message);
     70  });
     71 };`;
     72 
     73 function getChildText(opts) {
     74  if (opts.child === 'iframe') {
     75    if (opts.check === 'controller') {
     76      return frameControllerText;
     77    }
     78 
     79    if (opts.check === 'fetch') {
     80      return frameFetchText;
     81    }
     82 
     83    throw('unexpected feature to check: ' + opts.check);
     84  }
     85 
     86  if (opts.child === 'worker') {
     87    if (opts.check === 'controller') {
     88      return workerControllerText;
     89    }
     90 
     91    if (opts.check === 'fetch') {
     92      return workerFetchText;
     93    }
     94 
     95    throw('unexpected feature to check: ' + opts.check);
     96  }
     97 
     98  if (opts.child === 'sharedworker') {
     99    if (opts.check === 'controller') {
    100      return sharedWorkerControllerText;
    101    }
    102 
    103    if (opts.check === 'fetch') {
    104      return sharedWorkerFetchText;
    105    }
    106 
    107    throw('unexpected feature to check: ' + opts.check);
    108  }
    109 
    110  throw('unexpected child type ' + opts.child);
    111 }
    112 
    113 function makeURL(opts) {
    114  let mimetype = opts.child === 'iframe' ? 'text/html'
    115                                         : 'text/javascript';
    116 
    117  if (opts.scheme === 'blob') {
    118    let blob = new Blob([getChildText(opts)], { type: mimetype });
    119    return URL.createObjectURL(blob);
    120  }
    121 
    122  if (opts.scheme === 'data') {
    123    return `data:${mimetype},${getChildText(opts)}`;
    124  }
    125 
    126  throw(`unexpected URL scheme ${opts.scheme}`);
    127 }
    128 
    129 function testWorkerChild(url) {
    130  let w = new Worker(url);
    131  return new Promise((resolve, reject) => {
    132    w.onmessage = resolve;
    133    w.onerror = evt => {
    134      reject(evt.message);
    135    }
    136  });
    137 }
    138 
    139 function testSharedWorkerChild(url) {
    140  let w = new SharedWorker(url);
    141  return new Promise((resolve, reject) => {
    142    w.port.onmessage = m => {
    143      // (null is a valid value when the SharedWorker is not controlled)
    144      if (m.data?.includes("Error")) {
    145        reject(m.data);
    146        return;
    147      }
    148      resolve(m);
    149    }
    150    w.onerror = evt => {
    151      reject(evt.message);
    152    }
    153  });
    154 }
    155 
    156 function testIframeChild(url) {
    157  let frame = document.createElement('iframe');
    158  frame.src = url;
    159  document.body.appendChild(frame);
    160 
    161  return new Promise(resolve => {
    162    addEventListener('message', evt => {
    163      resolve(evt.data);
    164    }, { once: true });
    165  });
    166 }
    167 
    168 function testURL(opts, url) {
    169  if (opts.child === 'worker') {
    170    return testWorkerChild(url);
    171  }
    172 
    173  if (opts.child === 'sharedworker') {
    174    return testSharedWorkerChild(url);
    175  }
    176 
    177  if (opts.child === 'iframe') {
    178    return testIframeChild(url);
    179  }
    180 
    181  throw(`unexpected child type ${opts.child}`);
    182 }
    183 
    184 function checkChildController(opts) {
    185  let url = makeURL(opts);
    186  return testURL(opts, url);
    187 }
    188 </script>
    189 </html>