tor-browser

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

subresource-load.https.tentative.sub.html (11500B)


      1 <!DOCTYPE html>
      2 <title>Subresource loading with script type="webbundle"</title>
      3 <link
      4  rel="help"
      5  href="https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md"
      6 />
      7 <script src="/resources/testharness.js"></script>
      8 <script src="/resources/testharnessreport.js"></script>
      9 <script src="../resources/test-helpers.js"></script>
     10 <body>
     11  <script type="webbundle">
     12    {
     13      "source": "../resources/wbn/subresource.wbn",
     14      "resources": [
     15        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js",
     16        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/submodule.js"
     17      ]
     18    }
     19  </script>
     20  <script>
     21    setup(() => {
     22      assert_true(HTMLScriptElement.supports("webbundle"));
     23    });
     24 
     25    promise_test(async () => {
     26      const module = await import(
     27        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js"
     28      );
     29      assert_equals(module.result, "OK");
     30    }, "Subresource loading with WebBundle");
     31 
     32    promise_test(async () => {
     33      const response = await fetch(
     34        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js"
     35      );
     36      const text = await response.text();
     37      assert_equals(text, "export * from './submodule.js';\n");
     38    }, "Subresource loading with WebBundle (Fetch API)");
     39 
     40    promise_test((t) => {
     41      const url =
     42        "/common/redirect.py?location=https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/root.js";
     43      return promise_rejects_js(t, TypeError, import(url));
     44    }, "Subresource loading with WebBundle shouldn't affect redirect");
     45 
     46    promise_test(async () => {
     47      const element = createWebBundleElement("../resources/wbn/dynamic1.wbn", [
     48        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js",
     49        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource2.js",
     50        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource4.js",
     51      ]);
     52      document.body.appendChild(element);
     53 
     54      const module = await import(
     55        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js"
     56      );
     57      assert_equals(module.result, "resource1 from dynamic1.wbn");
     58 
     59      const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
     60        url: "../resources/wbn/dynamic2.wbn",
     61      });
     62      const module2 = await import(
     63        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource2.js"
     64      );
     65      assert_equals(module2.result, "resource2 from dynamic2.wbn");
     66 
     67      // A resource not specified in the resources attribute, but in the bundle.
     68      const module3 = await import(
     69        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource3.js"
     70      );
     71      assert_equals(module3.result, "resource3 from network");
     72 
     73      document.body.removeChild(new_element);
     74      const module4 = await import(
     75        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource4.js"
     76      );
     77      assert_equals(module4.result, "resource4 from network");
     78 
     79      // Module scripts are stored to the Document's module map once loaded.
     80      // So import()ing the same module script will reuse the previously loaded
     81      // script.
     82      const module_second = await import(
     83        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js"
     84      );
     85      assert_equals(module_second.result, "resource1 from dynamic1.wbn");
     86    }, "Dynamically adding / updating / removing the webbundle element.");
     87 
     88    promise_test(async () => {
     89      const classic_script_url =
     90        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js";
     91      const element = createWebBundleElement("../resources/wbn/dynamic1.wbn", [
     92        classic_script_url,
     93      ]);
     94      document.body.appendChild(element);
     95      assert_equals(
     96        await loadScriptAndWaitReport(classic_script_url),
     97        "classic script from dynamic1.wbn"
     98      );
     99      const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
    100        url: "../resources/wbn/dynamic2.wbn",
    101      });
    102      // Loading the classic script should not reuse the previously loaded
    103      // script. So in this case, the script must be loaded from dynamic2.wbn.
    104      assert_equals(
    105        await loadScriptAndWaitReport(classic_script_url),
    106        "classic script from dynamic2.wbn"
    107      );
    108      document.body.removeChild(new_element);
    109      // And in this case, the script must be loaded from network.
    110      assert_equals(
    111        await loadScriptAndWaitReport(classic_script_url),
    112        "classic script from network"
    113      );
    114    }, "Dynamically loading classic script from web bundle");
    115 
    116    promise_test(async (t) => {
    117      // To avoid caching mechanism, this test is using fetch() API with
    118      // { cache: 'no-store' } to load the resource.
    119      const classic_script_url =
    120        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js";
    121 
    122      assert_equals(
    123        await (await fetch(classic_script_url)).text(),
    124        "window.report_result('classic script from network');\n"
    125      );
    126 
    127      const element1 = createWebBundleElement("../resources/wbn/dynamic1.wbn", [
    128        classic_script_url,
    129      ]);
    130      document.body.appendChild(element1);
    131      t.add_cleanup(() => {
    132        if (element1.parentElement)
    133          element1.parentElement.removeChild(element1);
    134      });
    135 
    136      assert_equals(
    137        await (await fetch(classic_script_url, { cache: "no-store" })).text(),
    138        "window.report_result('classic script from dynamic1.wbn');\n"
    139      );
    140 
    141      const element2 = createWebBundleElement("../resources/wbn/dynamic2.wbn", [
    142        classic_script_url,
    143      ]);
    144      document.body.appendChild(element2);
    145      t.add_cleanup(() => {
    146        if (element2.parentElement)
    147          element2.parentElement.removeChild(element2);
    148      });
    149 
    150      assert_equals(
    151        await (await fetch(classic_script_url, { cache: "no-store" })).text(),
    152        "window.report_result('classic script from dynamic2.wbn');\n"
    153      );
    154 
    155      document.body.removeChild(element2);
    156 
    157      assert_equals(
    158        await (await fetch(classic_script_url, { cache: "no-store" })).text(),
    159        "window.report_result('classic script from dynamic1.wbn');\n"
    160      );
    161 
    162      document.body.removeChild(element1);
    163 
    164      assert_equals(
    165        await (await fetch(classic_script_url, { cache: "no-store" })).text(),
    166        "window.report_result('classic script from network');\n"
    167      );
    168    }, "Multiple web bundle elements. The last added element must be refered.");
    169 
    170    promise_test(async () => {
    171      const classic_script_url =
    172        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/classic_script.js";
    173      const scope =
    174        "https://{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/";
    175      const element = createWebBundleElement(
    176        "../resources/wbn/dynamic1.wbn",
    177        [],
    178        { scopes: [scope] }
    179      );
    180      document.body.appendChild(element);
    181      assert_equals(
    182        await loadScriptAndWaitReport(classic_script_url),
    183        "classic script from dynamic1.wbn"
    184      );
    185      const new_element = removeAndAppendNewElementWithUpdatedRule(element, {
    186        url: "../resources/wbn/dynamic2.wbn",
    187      });
    188      // Loading the classic script should not reuse the previously loaded
    189      // script. So in this case, the script must be loaded from dynamic2.wbn.
    190      assert_equals(
    191        await loadScriptAndWaitReport(classic_script_url),
    192        "classic script from dynamic2.wbn"
    193      );
    194      // Changes the scope not to hit the classic_script.js.
    195      const new_element2 = removeAndAppendNewElementWithUpdatedRule(
    196        new_element,
    197        { scopes: [scope + "dummy"] }
    198      );
    199      // And in this case, the script must be loaded from network.
    200      assert_equals(
    201        await loadScriptAndWaitReport(classic_script_url),
    202        "classic script from network"
    203      );
    204      // Adds the scope to hit the classic_script.js.
    205      const new_element3 = removeAndAppendNewElementWithUpdatedRule(
    206        new_element2,
    207        { scopes: [scope + "dummy", scope + "classic_"] }
    208      );
    209      assert_equals(
    210        await loadScriptAndWaitReport(classic_script_url),
    211        "classic script from dynamic2.wbn"
    212      );
    213      document.body.removeChild(new_element3);
    214      // And in this case, the script must be loaded from network.
    215      assert_equals(
    216        await loadScriptAndWaitReport(classic_script_url),
    217        "classic script from network"
    218      );
    219    }, "Dynamically loading classic script from web bundle with scopes");
    220 
    221    promise_test(() => {
    222      return addWebBundleElementAndWaitForLoad(
    223        "../resources/wbn/dynamic1.wbn?test-event",
    224        /*resources=*/ [],
    225        { crossOrigin: undefined }
    226      );
    227    }, "The webbundle element fires a load event on load success");
    228 
    229    promise_test((t) => {
    230      return addWebBundleElementAndWaitForError(
    231        "../resources/wbn/nonexistent.wbn",
    232        /*resources=*/ [],
    233        { crossOrigin: undefined }
    234      );
    235    }, "The webbundle element fires an error event on load failure");
    236 
    237    promise_test(async () => {
    238      const module_script_url =
    239        "https://www1.{{domains[]}}:{{ports[https][0]}}/web-bundle/resources/wbn/dynamic/resource1.js";
    240      const element = createWebBundleElement(
    241        "../resources/wbn/dynamic1-crossorigin.wbn",
    242        [module_script_url]
    243      );
    244      document.body.appendChild(element);
    245      const module = await import(module_script_url);
    246      assert_equals(module.result, "resource1 from network");
    247    }, "Subresource URL must be same-origin with bundle URL");
    248 
    249    promise_test(async () => {
    250      const url = "uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720";
    251      const element = createWebBundleElement(
    252        "../resources/wbn/uuid-in-package.wbn",
    253        [url]
    254      );
    255      document.body.appendChild(element);
    256      assert_equals(await loadScriptAndWaitReport(url), "OK");
    257      document.body.removeChild(element);
    258    }, "Subresource loading with uuid-in-package: URL with resources attribute");
    259 
    260    promise_test(async () => {
    261      const url = "uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720";
    262      const element = createWebBundleElement(
    263        "../resources/wbn/uuid-in-package.wbn",
    264        [],
    265        { scopes: ["uuid-in-package:"] }
    266      );
    267      document.body.appendChild(element);
    268      assert_equals(await loadScriptAndWaitReport(url), "OK");
    269      document.body.removeChild(element);
    270    }, "Subresource loading with uuid-in-package: URL with scopes attribute");
    271 
    272    async function loadScriptAndWaitReport(script_url) {
    273      const result_promise = new Promise((resolve) => {
    274        // This function will be called from script.js
    275        window.report_result = resolve;
    276      });
    277 
    278      const script = document.createElement("script");
    279      script.src = script_url;
    280      document.body.appendChild(script);
    281      return result_promise;
    282    }
    283 
    284    function removeAndAppendNewElementWithUpdatedRule(element, new_rule) {
    285      const new_element = createNewWebBundleElementWithUpdatedRule(
    286        element,
    287        new_rule
    288      );
    289      element.remove();
    290      document.body.appendChild(new_element);
    291      return new_element;
    292    }
    293  </script>
    294 </body>