/* global allowListed */ async function hasStorageAccessInitially() { let hasAccess = await document.hasStorageAccess(); ok(hasAccess, "Has storage access"); } async function noStorageAccessInitially() { let hasAccess = await document.hasStorageAccess(); ok(!hasAccess, "Doesn't yet have storage access"); } async function callRequestStorageAccess(callback, expectFail) { let dwu = SpecialPowers.getDOMWindowUtils(window); let helper = dwu.setHandlingUserInput(true); let origin = new URL(location.href).origin; let success = true; // We only grant storage exceptions when the reject tracker behavior is enabled. let rejectTrackers = [ SpecialPowers.Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER, SpecialPowers.Ci.nsICookieService .BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, ].includes( SpecialPowers.Services.prefs.getIntPref("network.cookie.cookieBehavior") ) && !isOnContentBlockingAllowList(); const TEST_ANOTHER_3RD_PARTY_ORIGIN = SpecialPowers.useRemoteSubframes ? "http://another-tracking.example.net" : "https://another-tracking.example.net"; // With another-tracking.example.net, we're same-eTLD+1, so the first try succeeds. if (origin != TEST_ANOTHER_3RD_PARTY_ORIGIN) { if (rejectTrackers) { let p; let threw = false; try { p = document.requestStorageAccess(); } catch (e) { threw = true; } finally { helper.destruct(); } ok(!threw, "requestStorageAccess should not throw"); try { if (callback) { if (expectFail) { await p.catch(_ => callback(dwu)); success = false; } else { await p.then(_ => callback(dwu)); } } else { await p; } } catch (e) { success = false; } ok(!success, "Should not have worked without user interaction"); await noStorageAccessInitially(); await interactWithTracker(); helper = dwu.setHandlingUserInput(true); } if ( SpecialPowers.Services.prefs.getIntPref( "network.cookie.cookieBehavior" ) == SpecialPowers.Ci.nsICookieService.BEHAVIOR_ACCEPT && !isOnContentBlockingAllowList() ) { try { if (callback) { if (expectFail) { await document.requestStorageAccess().catch(_ => callback(dwu)); success = false; } else { await document.requestStorageAccess().then(_ => callback(dwu)); } } else { await document.requestStorageAccess(); } } catch (e) { success = false; } finally { helper.destruct(); } ok(success, "Should not have thrown"); await hasStorageAccessInitially(); await interactWithTracker(); helper = dwu.setHandlingUserInput(true); } } let p; let threw = false; try { p = document.requestStorageAccess(); } catch (e) { threw = true; } finally { helper.destruct(); } let rejected = false; try { if (callback) { if (expectFail) { await p.catch(_ => callback(dwu)); rejected = true; } else { await p.then(_ => callback(dwu)); } } else { await p; } } catch (e) { rejected = true; } success = !threw && !rejected; let hasAccess = await document.hasStorageAccess(); is( hasAccess, success, "Should " + (success ? "" : "not ") + "have storage access now" ); if ( success && rejectTrackers && window.location.search != "?disableWaitUntilPermission" && origin != TEST_ANOTHER_3RD_PARTY_ORIGIN ) { // Wait until the permission is visible in parent process to avoid race // conditions. We don't need to wait the permission to be visible in content // processes since the content process doesn't rely on the permission to // know the storage access is updated. await waitUntilPermission( "http://example.net/browser/toolkit/components/antitracking/test/browser/page.html", "3rdPartyStorage^" + window.origin ); } return [threw, rejected]; } async function waitUntilPermission(url, name) { let originAttributes = SpecialPowers.isContentWindowPrivate(window) ? { privateBrowsingId: 1 } : {}; await new Promise(resolve => { let id = setInterval(async _ => { if ( await SpecialPowers.testPermission( name, SpecialPowers.Services.perms.ALLOW_ACTION, { url, originAttributes, } ) ) { clearInterval(id); resolve(); } }, 0); }); } async function interactWithTracker() { await new Promise(resolve => { let orionmessage = onmessage; onmessage = _ => { onmessage = orionmessage; resolve(); }; info("Let's interact with the tracker"); window.open( "/browser/toolkit/components/antitracking/test/browser/3rdPartyOpenUI.html?messageme" ); }); // Wait until the user interaction permission becomes visible in our process await waitUntilPermission(window.origin, "storageAccessAPI"); } function isOnContentBlockingAllowList() { // We directly check the window.allowListed here instead of checking the // permission. The allow list permission might not be available since it is // not in the preload list. return window.allowListed; }