tor-browser

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

test_subframe_stop_after_parent_error.js (4280B)


      1 "use strict";
      2 // Tests that pending subframe requests for an initial about:blank
      3 // document do not delay showing load errors (and possibly result in a
      4 // crash at docShell destruction) for failed document loads.
      5 
      6 const { PromiseTestUtils } = ChromeUtils.importESModule(
      7  "resource://testing-common/PromiseTestUtils.sys.mjs"
      8 );
      9 PromiseTestUtils.allowMatchingRejectionsGlobally(/undefined/);
     10 
     11 const { XPCShellContentUtils } = ChromeUtils.importESModule(
     12  "resource://testing-common/XPCShellContentUtils.sys.mjs"
     13 );
     14 
     15 XPCShellContentUtils.init(this);
     16 
     17 const server = XPCShellContentUtils.createHttpServer({
     18  hosts: ["example.com"],
     19 });
     20 
     21 // Registers a URL with the HTTP server which will not return a response
     22 // until we're ready to.
     23 function registerSlowPage(path) {
     24  let result = {
     25    url: `http://example.com/${path}`,
     26  };
     27 
     28  let finishedPromise = new Promise(resolve => {
     29    result.finish = resolve;
     30  });
     31 
     32  server.registerPathHandler(`/${path}`, async (request, response) => {
     33    response.processAsync();
     34 
     35    response.setHeader("Content-Type", "text/html");
     36    response.write("<html><body>Hello.</body></html>");
     37 
     38    await finishedPromise;
     39 
     40    response.finish();
     41  });
     42 
     43  return result;
     44 }
     45 
     46 let topFrameRequest = registerSlowPage("top.html");
     47 let subFrameRequest = registerSlowPage("frame.html");
     48 
     49 let thunks = new Set();
     50 function promiseStateStop(webProgress) {
     51  return new Promise(resolve => {
     52    let listener = {
     53      onStateChange(aWebProgress, request, stateFlags) {
     54        if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
     55          webProgress.removeProgressListener(listener);
     56 
     57          thunks.delete(listener);
     58          resolve();
     59        }
     60      },
     61 
     62      QueryInterface: ChromeUtils.generateQI([
     63        "nsIWebProgressListener",
     64        "nsISupportsWeakReference",
     65      ]),
     66    };
     67 
     68    // Keep the listener alive, since it's stored as a weak reference.
     69    thunks.add(listener);
     70    webProgress.addProgressListener(
     71      listener,
     72      Ci.nsIWebProgress.NOTIFY_STATE_NETWORK
     73    );
     74  });
     75 }
     76 
     77 async function runTest(waitForErrorPage) {
     78  let page = await XPCShellContentUtils.loadContentPage("about:blank");
     79 
     80  // Watch for the HTTP request for the top frame so that we can cancel
     81  // it with an error.
     82  let requestPromise = TestUtils.topicObserved(
     83    "http-on-modify-request",
     84    subject => subject.QueryInterface(Ci.nsIRequest).name == topFrameRequest.url
     85  );
     86 
     87  await page.spawn(
     88    [topFrameRequest.url, subFrameRequest.url],
     89    function (topFrameUrl, subFrameRequestUrl) {
     90      // Create a frame with a source URL which will not return a response
     91      // before we cancel it with an error.
     92      let doc = this.content.document;
     93      let frame = doc.createElement("iframe");
     94      frame.src = topFrameUrl;
     95      doc.body.appendChild(frame);
     96 
     97      // Create a subframe in the initial about:blank document for the above
     98      // frame which will not return a response before we cancel the
     99      // document request.
    100      let frameDoc = frame.contentDocument;
    101      let subframe = frameDoc.createElement("iframe");
    102      subframe.src = subFrameRequestUrl;
    103      frameDoc.body.appendChild(subframe);
    104    }
    105  );
    106 
    107  let [req] = await requestPromise;
    108 
    109  info("Cancel request for parent frame");
    110  req.cancel(Cr.NS_ERROR_PROXY_CONNECTION_REFUSED);
    111 
    112  // Request cancelation is not synchronous, so wait for the STATE_STOP
    113  // event to fire.
    114  await promiseStateStop(page.browsingContext.webProgress);
    115 
    116  // And make a trip through the event loop to give the DocLoader a
    117  // chance to update its state.
    118  await new Promise(executeSoon);
    119 
    120  if (waitForErrorPage) {
    121    // Make sure that canceling the request with an error code actually
    122    // shows an error page without waiting for a subframe response.
    123    await TestUtils.waitForCondition(() =>
    124      page.browsingContext.children[0]?.currentWindowGlobal?.documentURI?.spec.startsWith(
    125        "about:neterror?"
    126      )
    127    );
    128  }
    129 
    130  await page.close();
    131 }
    132 
    133 add_task(async function testRemoveFrameImmediately() {
    134  await runTest(false);
    135 });
    136 
    137 add_task(async function testRemoveFrameAfterErrorPage() {
    138  await runTest(true);
    139 });
    140 
    141 add_task(async function () {
    142  // Allow the document requests for the frames to complete.
    143  topFrameRequest.finish();
    144  subFrameRequest.finish();
    145 });