function TestCase(scenarios, sanityChecker) { function runTest(scenario) { sanityChecker.checkScenario(scenario, subresourceMap); const urls = getRequestURLs(scenario.subresource, scenario.origin, scenario.redirection); /** @type {Subresource} */ const subresource = { subresourceType: scenario.subresource, url: urls.testUrl, policyDeliveries: scenario.subresource_policy_deliveries, }; let violationEventResolve; // Resolved with an array of securitypolicyviolation events. const violationEventPromise = new Promise(resolve => { violationEventResolve = resolve; }); promise_test(async t => { await xhrRequest(urls.announceUrl); // Currently only requests from top-level Documents are tested // (specified by `spec.src.json`) and thus securitypolicyviolation // events are assumed to be fired on the top-level Document here. // When adding non-top-level Document tests, securitypolicyviolation // events should be caught in appropriate contexts. const violationEvents = []; const listener = e => { violationEvents.push(e); }; document.addEventListener('securitypolicyviolation', listener); try { // Send out the real resource request. // This should tear down the key if it's not blocked. const mainPromise = invokeRequest(subresource, scenario.source_context_list); if (scenario.expectation === 'allowed') { await mainPromise; } else { await mainPromise .then(t.unreached_func('main promise resolved unexpectedly')) .catch(_ => {}); } } finally { // Always perform post-processing/clean up for // 'securitypolicyviolation' events and resolve // `violationEventPromise`, to prevent timeout of the // promise_test() below. // securitypolicyviolation events are fired in a queued task in // https://w3c.github.io/webappsec-csp/#report-violation // so wait for queued tasks to run using setTimeout(). let timeout = 0; if (scenario.subresource.startsWith('worklet-') && navigator.userAgent.includes("Firefox/")) { // https://bugzilla.mozilla.org/show_bug.cgi?id=1808911 // In Firefox sometimes violations from Worklets are delayed. timeout = 10; } await new Promise(resolve => setTimeout(resolve, timeout)); // Pass violation events to `violationEventPromise` (which will be tested // in the subsequent promise_test()) and clean up the listener. violationEventResolve(violationEvents); document.removeEventListener('securitypolicyviolation', listener); } // Send request to check if the key has been torn down. const assertResult = await xhrRequest(urls.assertUrl); // Now check if the value has been torn down. If it's still there, // we have blocked the request by content security policy. assert_equals(assertResult.status, scenario.expectation, "The resource request should be '" + scenario.expectation + "'."); }, scenario.test_description); promise_test(async _ => { const violationEvents = await violationEventPromise; if (scenario.expectation === 'allowed') { assert_array_equals(violationEvents, [], 'no violation events should be fired'); } else { assert_equals(violationEvents.length, 1, 'One violation event should be fired'); } }, scenario.test_description + ": securitypolicyviolation"); } // runTest function runTests() { for (const scenario of scenarios) { runTest(scenario); } } return {start: runTests}; }