tor-browser

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

browser_scriptCache_perf_timeline.js (19742B)


      1 const URL_BASE = "https://example.com/browser/dom/tests/browser/";
      2 const URL_BASE2 = "https://example.net/browser/dom/tests/browser/";
      3 
      4 function testFields(
      5  entry,
      6  { hasBodyAccess, hasTimingAccess, isCacheOf },
      7  desc
      8 ) {
      9  Assert.equal(entry.entryType, "resource", "entryType should be available");
     10  Assert.equal(
     11    entry.initiatorType,
     12    "script",
     13    "initiatorType should be available"
     14  );
     15 
     16  if (hasTimingAccess) {
     17    Assert.equal(
     18      entry.nextHopProtocol,
     19      "http/1.1",
     20      `nextHopProtocol should be available for ${desc}`
     21    );
     22  } else {
     23    Assert.equal(
     24      entry.nextHopProtocol,
     25      "",
     26      `nextHopProtocol should be hidden for ${desc}`
     27    );
     28  }
     29 
     30  if (hasBodyAccess) {
     31    Assert.equal(
     32      entry.responseStatus,
     33      200,
     34      `responseStatus should be available for ${desc}`
     35    );
     36  } else {
     37    Assert.equal(
     38      entry.responseStatus,
     39      0,
     40      `responseStatus should be hidden for ${desc}`
     41    );
     42  }
     43 
     44  if (hasBodyAccess) {
     45    Assert.equal(
     46      entry.contentType,
     47      "text/javascript",
     48      `contentType should be available for ${desc}`
     49    );
     50  } else {
     51    Assert.equal(
     52      entry.contentType,
     53      "",
     54      `contentType should be hidden for ${desc}`
     55    );
     56  }
     57 
     58  Assert.greater(
     59    entry.startTime,
     60    0,
     61    `startTime should be non-zero for ${desc}`
     62  );
     63  Assert.greater(
     64    entry.responseEnd,
     65    0,
     66    `responseEnd should be non-zero for ${desc}`
     67  );
     68  Assert.lessOrEqual(
     69    entry.startTime,
     70    entry.responseEnd,
     71    `startTime <= responseEnd for ${desc}`
     72  );
     73 
     74  if (hasTimingAccess) {
     75    Assert.deepEqual(
     76      entry.serverTiming,
     77      [
     78        { name: "name1", duration: 0, description: "" },
     79        { name: "name2", duration: 20, description: "" },
     80        { name: "name3", duration: 30, description: "desc3" },
     81      ],
     82      `serverTiming should be available for ${desc}`
     83    );
     84  } else {
     85    Assert.deepEqual(
     86      entry.serverTiming,
     87      [],
     88      `serverTiming should be hidden for ${desc}`
     89    );
     90  }
     91 
     92  if (hasBodyAccess) {
     93    Assert.greater(
     94      entry.encodedBodySize,
     95      0,
     96      `encodedBodySize should be available for ${desc}`
     97    );
     98  } else {
     99    Assert.equal(
    100      entry.encodedBodySize,
    101      0,
    102      `encodedBodySize should be hidden for ${desc}`
    103    );
    104  }
    105 
    106  if (isCacheOf) {
    107    Assert.equal(
    108      entry.encodedBodySize,
    109      isCacheOf.encodedBodySize,
    110      `encodedBodySize should equal to non-cache case for ${desc}`
    111    );
    112  }
    113 
    114  if (hasBodyAccess) {
    115    Assert.greater(
    116      entry.decodedBodySize,
    117      0,
    118      `decodedBodySize should be available for ${desc}`
    119    );
    120  } else {
    121    Assert.equal(
    122      entry.decodedBodySize,
    123      0,
    124      `decodedBodySize should be hidden for ${desc}`
    125    );
    126  }
    127 
    128  if (isCacheOf) {
    129    Assert.equal(
    130      entry.decodedBodySize,
    131      isCacheOf.decodedBodySize,
    132      `decodedBodySize should equal to non-cache case for ${desc}`
    133    );
    134  }
    135 
    136  if (hasTimingAccess) {
    137    if (isCacheOf) {
    138      Assert.equal(
    139        entry.transferSize,
    140        0,
    141        `transferSize should be zero for ${desc}`
    142      );
    143    } else if (hasBodyAccess) {
    144      Assert.greater(
    145        entry.transferSize,
    146        300,
    147        `transferSize should be non-zero +300 for ${desc}`
    148      );
    149    } else {
    150      Assert.equal(
    151        entry.transferSize,
    152        300,
    153        `transferSize should be zero +300 for ${desc}`
    154      );
    155    }
    156  } else {
    157    Assert.equal(
    158      entry.transferSize,
    159      0,
    160      `transferSize should be hidden for ${desc}`
    161    );
    162  }
    163 }
    164 
    165 async function doTestCompleteCacheAfterReload(type) {
    166  await SpecialPowers.pushPrefEnv({
    167    set: [["dom.script_loader.experimental.navigation_cache", true]],
    168  });
    169  registerCleanupFunction(() => SpecialPowers.popPrefEnv());
    170 
    171  ChromeUtils.clearResourceCache();
    172  Services.cache2.clear();
    173 
    174  await BrowserTestUtils.withNewTab(
    175    {
    176      gBrowser,
    177      url: URL_BASE + "dummy.html",
    178    },
    179    async function (browser) {
    180      const JS_URL = URL_BASE + "perf_server.sjs?cacheable";
    181 
    182      const task = async (url, type) => {
    183        await new Promise(resolve => {
    184          const script = content.document.createElement("script");
    185          script.src = url;
    186          if (type === "module") {
    187            script.type = "module";
    188          }
    189          script.addEventListener("load", resolve);
    190          content.document.head.append(script);
    191        });
    192 
    193        const entries = content.performance
    194          .getEntriesByType("resource")
    195          .filter(entry => entry.name.includes("perf_server.sjs"));
    196        if (entries.length != 1) {
    197          throw new Error(`Expect one entry, got ${entries.length} entries`);
    198        }
    199        // NOTE: entries[0].toJSON() doesn't convert serverTiming items.
    200        return JSON.parse(JSON.stringify(entries[0]));
    201      };
    202 
    203      const entry = await SpecialPowers.spawn(browser, [JS_URL, type], task);
    204      Assert.equal(entry.name, JS_URL);
    205      testFields(
    206        entry,
    207        {
    208          hasBodyAccess: true,
    209          hasTimingAccess: true,
    210        },
    211        "same origin (non-cached)"
    212      );
    213 
    214      await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
    215 
    216      const cacheEntry = await SpecialPowers.spawn(
    217        browser,
    218        [JS_URL, type],
    219        task
    220      );
    221      Assert.equal(cacheEntry.name, JS_URL);
    222      testFields(
    223        cacheEntry,
    224        {
    225          hasBodyAccess: true,
    226          hasTimingAccess: true,
    227          isCacheOf: entry,
    228        },
    229        "same origin (cached)"
    230      );
    231    }
    232  );
    233 }
    234 
    235 add_task(async function testScriptCompleteCacheAfterReload() {
    236  await doTestCompleteCacheAfterReload("script");
    237 });
    238 add_task(async function testModuleCompleteCacheAfterReload() {
    239  await doTestCompleteCacheAfterReload("module");
    240 });
    241 
    242 async function doTestCompleteCacheInSameDocument(type) {
    243  await SpecialPowers.pushPrefEnv({
    244    set: [["dom.script_loader.experimental.navigation_cache", true]],
    245  });
    246  registerCleanupFunction(() => SpecialPowers.popPrefEnv());
    247 
    248  ChromeUtils.clearResourceCache();
    249  Services.cache2.clear();
    250 
    251  await BrowserTestUtils.withNewTab(
    252    {
    253      gBrowser,
    254      url: URL_BASE + "dummy.html",
    255    },
    256    async function (browser) {
    257      const JS_URL = URL_BASE + "perf_server.sjs?cacheable";
    258 
    259      const task = async (url, type) => {
    260        // Before reload:
    261        //   * The first load is not cache
    262        //   * The second load is complete cache
    263        // After reload:
    264        //   * Both loads are complete cache
    265 
    266        for (let i = 0; i < 2; i++) {
    267          await new Promise(resolve => {
    268            const script = content.document.createElement("script");
    269            script.src = url;
    270            if (type === "module") {
    271              script.type = "module";
    272            }
    273            script.addEventListener("load", () => {
    274              resolve();
    275            });
    276            content.document.head.append(script);
    277          });
    278        }
    279 
    280        const entries = content.performance
    281          .getEntriesByType("resource")
    282          .filter(entry => entry.name.includes("perf_server.sjs"));
    283        // In contrast to CSS, classic scripts perform "fetch" for both.
    284        // Module scripts become singleton, and only one "fetch" happens.
    285        if (type === "script") {
    286          if (entries.length != 2) {
    287            throw new Error(
    288              `Expect two entries, got ${entries.length} entries`
    289            );
    290          }
    291        } else if (entries.length != 1) {
    292          throw new Error(`Expect one entry, got ${entries.length} entries`);
    293        }
    294        return JSON.parse(JSON.stringify(entries));
    295      };
    296 
    297      const entries = await SpecialPowers.spawn(browser, [JS_URL, type], task);
    298      Assert.equal(entries[0].name, JS_URL);
    299      if (type === "script") {
    300        Assert.equal(entries[1].name, JS_URL);
    301      }
    302      testFields(
    303        entries[0],
    304        {
    305          hasBodyAccess: true,
    306          hasTimingAccess: true,
    307        },
    308        "same origin (non-cached)"
    309      );
    310      if (type === "script") {
    311        testFields(
    312          entries[1],
    313          {
    314            hasBodyAccess: true,
    315            hasTimingAccess: true,
    316            isCacheOf: entries[0],
    317          },
    318          "same origin (cached)"
    319        );
    320      }
    321 
    322      await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
    323 
    324      const cacheEntries = await SpecialPowers.spawn(
    325        browser,
    326        [JS_URL, type],
    327        task
    328      );
    329      Assert.equal(cacheEntries[0].name, JS_URL);
    330      if (type === "script") {
    331        Assert.equal(cacheEntries[1].name, JS_URL);
    332      }
    333      testFields(
    334        cacheEntries[0],
    335        {
    336          hasBodyAccess: true,
    337          hasTimingAccess: true,
    338          isCacheOf: entries[0],
    339        },
    340        "same origin (cached)"
    341      );
    342      if (type === "script") {
    343        testFields(
    344          cacheEntries[1],
    345          {
    346            hasBodyAccess: true,
    347            hasTimingAccess: true,
    348            isCacheOf: entries[0],
    349          },
    350          "same origin (cached)"
    351        );
    352      }
    353    }
    354  );
    355 }
    356 
    357 add_task(async function testScriptCompleteCacheInSameDocument() {
    358  await doTestCompleteCacheInSameDocument("script");
    359 });
    360 add_task(async function testModuleCompleteCacheInSameDocument() {
    361  await doTestCompleteCacheInSameDocument("module");
    362 });
    363 
    364 async function doTestNoCacheReload(type) {
    365  await SpecialPowers.pushPrefEnv({
    366    set: [["dom.script_loader.experimental.navigation_cache", true]],
    367  });
    368  registerCleanupFunction(() => SpecialPowers.popPrefEnv());
    369 
    370  ChromeUtils.clearResourceCache();
    371  Services.cache2.clear();
    372 
    373  await BrowserTestUtils.withNewTab(
    374    {
    375      gBrowser,
    376      url: URL_BASE + "dummy.html",
    377    },
    378    async function (browser) {
    379      const JS_URL = URL_BASE + "perf_server.sjs?";
    380 
    381      const task = async (url, type) => {
    382        await new Promise(resolve => {
    383          const script = content.document.createElement("script");
    384          script.src = url;
    385          if (type === "module") {
    386            script.type = "module";
    387          }
    388          script.addEventListener("load", resolve);
    389          content.document.head.append(script);
    390        });
    391 
    392        const entries = content.performance
    393          .getEntriesByType("resource")
    394          .filter(entry => entry.name.includes("perf_server.sjs"));
    395        if (entries.length != 1) {
    396          throw new Error(`Expect one entry, got ${entries.length} entries`);
    397        }
    398        return JSON.parse(JSON.stringify(entries[0]));
    399      };
    400 
    401      const entry = await SpecialPowers.spawn(browser, [JS_URL, type], task);
    402      Assert.equal(entry.name, JS_URL);
    403      testFields(
    404        entry,
    405        {
    406          hasBodyAccess: true,
    407          hasTimingAccess: true,
    408        },
    409        "same origin (non-cached)"
    410      );
    411 
    412      await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
    413 
    414      // Reloading the JS shouldn't hit any cache.
    415 
    416      const reloadEntry = await SpecialPowers.spawn(
    417        browser,
    418        [JS_URL, type],
    419        task
    420      );
    421      Assert.equal(reloadEntry.name, JS_URL);
    422      testFields(
    423        reloadEntry,
    424        {
    425          hasBodyAccess: true,
    426          hasTimingAccess: true,
    427        },
    428        "same origin (non-cached)"
    429      );
    430    }
    431  );
    432 }
    433 
    434 add_task(async function testScriptNoCacheReload() {
    435  await doTestNoCacheReload("script");
    436 });
    437 add_task(async function testModuleNoCacheReload() {
    438  await doTestNoCacheReload("module");
    439 });
    440 
    441 async function doTest_NoCORS(type) {
    442  await SpecialPowers.pushPrefEnv({
    443    set: [["dom.script_loader.experimental.navigation_cache", true]],
    444  });
    445  registerCleanupFunction(() => SpecialPowers.popPrefEnv());
    446 
    447  ChromeUtils.clearResourceCache();
    448  Services.cache2.clear();
    449 
    450  await BrowserTestUtils.withNewTab(
    451    {
    452      gBrowser,
    453      url: URL_BASE + "dummy.html",
    454    },
    455    async function (browser) {
    456      const JS_URL = URL_BASE2 + "perf_server.sjs?cacheable";
    457 
    458      const task = async (url, type) => {
    459        await new Promise(resolve => {
    460          const script = content.document.createElement("script");
    461          script.src = url;
    462          if (type === "module") {
    463            script.type = "module";
    464          }
    465          script.addEventListener("load", resolve);
    466          content.document.head.append(script);
    467        });
    468 
    469        const entries = content.performance
    470          .getEntriesByType("resource")
    471          .filter(entry => entry.name.includes("perf_server.sjs"));
    472        if (entries.length != 1) {
    473          throw new Error(`Expect one entry, got ${entries.length} entries`);
    474        }
    475        return JSON.parse(JSON.stringify(entries[0]));
    476      };
    477 
    478      const entry = await SpecialPowers.spawn(browser, [JS_URL, type], task);
    479      Assert.equal(entry.name, JS_URL);
    480      testFields(
    481        entry,
    482        {
    483          hasBodyAccess: false,
    484          hasTimingAccess: false,
    485        },
    486        "cross origin (non-cached)"
    487      );
    488 
    489      await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
    490 
    491      const cacheEntry = await SpecialPowers.spawn(
    492        browser,
    493        [JS_URL, type],
    494        task
    495      );
    496      Assert.equal(cacheEntry.name, JS_URL);
    497      testFields(
    498        cacheEntry,
    499        {
    500          hasBodyAccess: false,
    501          hasTimingAccess: false,
    502          isCacheOf: entry,
    503        },
    504        "cross origin (cached)"
    505      );
    506    }
    507  );
    508 }
    509 
    510 add_task(async function testScript_NoCORS() {
    511  await doTest_NoCORS("script");
    512 });
    513 // Modules cannot be loaded with no CORS.
    514 
    515 async function doTest_NoCORS_TAO(type) {
    516  await SpecialPowers.pushPrefEnv({
    517    set: [["dom.script_loader.experimental.navigation_cache", true]],
    518  });
    519  registerCleanupFunction(() => SpecialPowers.popPrefEnv());
    520 
    521  ChromeUtils.clearResourceCache();
    522  Services.cache2.clear();
    523 
    524  await BrowserTestUtils.withNewTab(
    525    {
    526      gBrowser,
    527      url: URL_BASE + "dummy.html",
    528    },
    529    async function (browser) {
    530      const JS_URL = URL_BASE2 + "perf_server.sjs?cacheable,tao";
    531 
    532      const task = async (url, type) => {
    533        await new Promise(resolve => {
    534          const script = content.document.createElement("script");
    535          script.src = url;
    536          if (type === "module") {
    537            script.type = "module";
    538          }
    539          script.addEventListener("load", resolve);
    540          content.document.head.append(script);
    541        });
    542 
    543        const entries = content.performance
    544          .getEntriesByType("resource")
    545          .filter(entry => entry.name.includes("perf_server.sjs"));
    546        if (entries.length != 1) {
    547          throw new Error(`Expect one entry, got ${entries.length} entries`);
    548        }
    549        return JSON.parse(JSON.stringify(entries[0]));
    550      };
    551 
    552      const entry = await SpecialPowers.spawn(browser, [JS_URL, type], task);
    553      Assert.equal(entry.name, JS_URL);
    554      testFields(
    555        entry,
    556        {
    557          hasBodyAccess: false,
    558          hasTimingAccess: true,
    559        },
    560        "cross origin with Timing-Allow-Origin (non-cached)"
    561      );
    562 
    563      await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
    564 
    565      const cacheEntry = await SpecialPowers.spawn(
    566        browser,
    567        [JS_URL, type],
    568        task
    569      );
    570      Assert.equal(cacheEntry.name, JS_URL);
    571      testFields(
    572        cacheEntry,
    573        {
    574          hasBodyAccess: false,
    575          hasTimingAccess: true,
    576          isCacheOf: entry,
    577        },
    578        "cross origin with Timing-Allow-Origin (cached)"
    579      );
    580    }
    581  );
    582 }
    583 
    584 add_task(async function testScript_NoCORS_TAO() {
    585  await doTest_NoCORS_TAO("script");
    586 });
    587 // Modules cannot be loaded with no CORS.
    588 
    589 async function doTest_CORS(type) {
    590  await SpecialPowers.pushPrefEnv({
    591    set: [["dom.script_loader.experimental.navigation_cache", true]],
    592  });
    593  registerCleanupFunction(() => SpecialPowers.popPrefEnv());
    594 
    595  ChromeUtils.clearResourceCache();
    596  Services.cache2.clear();
    597 
    598  await BrowserTestUtils.withNewTab(
    599    {
    600      gBrowser,
    601      url: URL_BASE + "dummy.html",
    602    },
    603    async function (browser) {
    604      const JS_URL = URL_BASE2 + "perf_server.sjs?cacheable,cors";
    605 
    606      const task = async (url, type) => {
    607        await new Promise(resolve => {
    608          const script = content.document.createElement("script");
    609          script.setAttribute("crossorigin", "anonymous");
    610          script.src = url;
    611          if (type === "module") {
    612            script.type = "module";
    613          }
    614          script.addEventListener("load", resolve);
    615          content.document.head.append(script);
    616        });
    617 
    618        const entries = content.performance
    619          .getEntriesByType("resource")
    620          .filter(entry => entry.name.includes("perf_server.sjs"));
    621        if (entries.length != 1) {
    622          throw new Error(`Expect one entry, got ${entries.length} entries`);
    623        }
    624        return JSON.parse(JSON.stringify(entries[0]));
    625      };
    626 
    627      const entry = await SpecialPowers.spawn(browser, [JS_URL, type], task);
    628      Assert.equal(entry.name, JS_URL);
    629      testFields(
    630        entry,
    631        {
    632          hasBodyAccess: true,
    633          hasTimingAccess: false,
    634        },
    635        "CORS (non-cached)"
    636      );
    637 
    638      await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
    639 
    640      const cacheEntry = await SpecialPowers.spawn(
    641        browser,
    642        [JS_URL, type],
    643        task
    644      );
    645      Assert.equal(cacheEntry.name, JS_URL);
    646      testFields(
    647        cacheEntry,
    648        {
    649          hasBodyAccess: true,
    650          hasTimingAccess: false,
    651          isCacheOf: entry,
    652        },
    653        "CORS (cached)"
    654      );
    655    }
    656  );
    657 }
    658 
    659 add_task(async function testScript_CORS() {
    660  await doTest_CORS("script");
    661 });
    662 add_task(async function testModule_CORS() {
    663  await doTest_CORS("module");
    664 });
    665 
    666 async function doTest_CORS_TAO(type) {
    667  await SpecialPowers.pushPrefEnv({
    668    set: [["dom.script_loader.experimental.navigation_cache", true]],
    669  });
    670  registerCleanupFunction(() => SpecialPowers.popPrefEnv());
    671 
    672  ChromeUtils.clearResourceCache();
    673  Services.cache2.clear();
    674 
    675  await BrowserTestUtils.withNewTab(
    676    {
    677      gBrowser,
    678      url: URL_BASE + "dummy.html",
    679    },
    680    async function (browser) {
    681      const JS_URL = URL_BASE2 + "perf_server.sjs?cacheable,cors,tao";
    682 
    683      const task = async (url, type) => {
    684        await new Promise(resolve => {
    685          const script = content.document.createElement("script");
    686          script.setAttribute("crossorigin", "anonymous");
    687          script.src = url;
    688          if (type === "module") {
    689            script.type = "module";
    690          }
    691          script.addEventListener("load", resolve);
    692          content.document.head.append(script);
    693        });
    694 
    695        const entries = content.performance
    696          .getEntriesByType("resource")
    697          .filter(entry => entry.name.includes("perf_server.sjs"));
    698        if (entries.length != 1) {
    699          throw new Error(`Expect one entry, got ${entries.length} entries`);
    700        }
    701        return JSON.parse(JSON.stringify(entries[0]));
    702      };
    703 
    704      const entry = await SpecialPowers.spawn(browser, [JS_URL, type], task);
    705      Assert.equal(entry.name, JS_URL);
    706      testFields(
    707        entry,
    708        {
    709          hasBodyAccess: true,
    710          hasTimingAccess: true,
    711        },
    712        "CORS with Timing-Allow-Origin (non-cached)"
    713      );
    714 
    715      await BrowserTestUtils.reloadTab(gBrowser.selectedTab);
    716 
    717      const cacheEntry = await SpecialPowers.spawn(
    718        browser,
    719        [JS_URL, type],
    720        task
    721      );
    722      Assert.equal(cacheEntry.name, JS_URL);
    723      testFields(
    724        cacheEntry,
    725        {
    726          hasBodyAccess: true,
    727          hasTimingAccess: true,
    728          isCacheOf: entry,
    729        },
    730        "CORS with Timing-Allow-Origin (cached)"
    731      );
    732    }
    733  );
    734 }
    735 
    736 add_task(async function testScript_CORS_TAO() {
    737  await doTest_CORS_TAO("script");
    738 });
    739 add_task(async function testModule_CORS_TAO() {
    740  await doTest_CORS_TAO("module");
    741 });