// META: script=/resources/testdriver.js // META: script=/common/utils.js // META: script=resources/fledge-util.sub.js // META: script=/common/subset-tests.js // META: timeout=long // META: variant=?1-4 // META: variant=?5-8 // META: variant=?9-12 // META: variant=?13-last "use strict;" /////////////////////////////////////////////////////////////////////////////// // Basic tests with no interest groups joined. /////////////////////////////////////////////////////////////////////////////// subsetTest(promise_test, async test => { await navigator.clearOriginJoinedAdInterestGroups(window.location.origin); }, 'clearOriginJoinedAdInterestGroups(), no groups joined, no group list.'); subsetTest(promise_test, async test => { await navigator.clearOriginJoinedAdInterestGroups(window.location.origin, []); }, 'clearOriginJoinedAdInterestGroups(), no groups joined, group list.'); subsetTest(promise_test, async test => { try { await navigator.clearOriginJoinedAdInterestGroups(OTHER_ORIGIN1); throw 'Exception unexpectedly not thrown'; } catch (e) { if (!(e instanceof DOMException) || e.name !== 'NotAllowedError') { throw 'Wrong exception thrown: ' + e.toString(); } } }, 'clearOriginJoinedAdInterestGroups(), cross-origin, no groups joined, no group list.'); subsetTest(promise_test, async test => { try { await navigator.clearOriginJoinedAdInterestGroups(OTHER_ORIGIN1, []); throw 'Exception unexpectedly not thrown'; } catch (e) { if (!(e instanceof DOMException) || e.name !== 'NotAllowedError') { throw 'Wrong exception thrown: ' + e.toString(); } } }, 'clearOriginJoinedAdInterestGroups(), cross-origin, no groups joined, group list.'); /////////////////////////////////////////////////////////////////////////////// // Tests where interest groups are all owned by document.location.origin. /////////////////////////////////////////////////////////////////////////////// subsetTest(promise_test, async test => { const uuid = generateUuid(test); // Join 3 groups. await joinInterestGroup(test, uuid); await joinInterestGroup(test, uuid, {name: 'group 2'}); await joinInterestGroup(test, uuid, {name: 'group 3'}); // A single clear should leave them all. await navigator.clearOriginJoinedAdInterestGroups(window.location.origin); // Confirm that they were left. await runBasicFledgeTestExpectingNoWinner(test, uuid); }, 'clearOriginJoinedAdInterestGroups(), multiple groups joined, no group list.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); let group1ReportURL = createBidderReportURL(uuid, /*id=*/'1'); let group2ReportURL = createBidderReportURL(uuid, /*id=*/'2'); let group3ReportURL = createBidderReportURL(uuid, /*id=*/'3'); // Join 3 groups, with distinct report URLs and increasing bid amounts. // Set "executionMode" to "group-by-origin" for two of them, since cross-origin // leaves removes all groups joined from the other origin with that execution // mode. Since clearOriginJoinedAdInterestGroups() only leaves interest // groups joined on the current origin, the executionMode should not matter. await joinInterestGroup( test, uuid, { name: 'group 1', executionMode: 'group-by-origin', biddingLogicURL: createBiddingScriptURL( { bid: 1, reportWin: `sendReportTo("${group1ReportURL}");`})}); await joinInterestGroup( test, uuid, { name: 'group 2', biddingLogicURL: createBiddingScriptURL( { bid: 2, reportWin: `sendReportTo("${group2ReportURL}");`})}); await joinInterestGroup( test, uuid, { name: 'group 3', executionMode: 'group-by-origin', biddingLogicURL: createBiddingScriptURL( { bid: 3, reportWin: `sendReportTo("${group3ReportURL}");`})}); // Group 3 should win an auction, since it bids the most. await runBasicFledgeAuctionAndNavigate(test, uuid); await waitForObservedRequests( uuid, [group3ReportURL, createSellerReportURL(uuid)]); await fetch(createCleanupURL(uuid)); // Clear, leaving group 1 in place, and run an auction, which group 1 should win. await navigator.clearOriginJoinedAdInterestGroups( window.location.origin, ['group 1']); await runBasicFledgeAuctionAndNavigate(test, uuid); await waitForObservedRequests( uuid, [group1ReportURL, createSellerReportURL(uuid)]); // Clear with an empty list, which should leave group 1 as well. Verify it can't // win an auction. await navigator.clearOriginJoinedAdInterestGroups(window.location.origin, []); await runBasicFledgeTestExpectingNoWinner(test, uuid); }, 'clearOriginJoinedAdInterestGroups(), multiple groups joined, group list.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); // Join an interest group in a same-origin top-level window. await joinInterestGroupInTopLevelWindow(test, uuid, window.location.origin); // Make sure it was joined. await runBasicFledgeTestExpectingWinner(test, uuid); // Call "clearOriginJoinedAdInterestGroups()", which should leave the interest // group, since it was joined from a same-origin main frame. await navigator.clearOriginJoinedAdInterestGroups(window.location.origin); // Make sure group was left. await runBasicFledgeTestExpectingNoWinner(test, uuid); }, 'clearOriginJoinedAdInterestGroups(), group joined from same-origin top-level context.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); // Create top-level browsing context for another origin, and have it join an // interest group owned by this document's origin. let topLevelWindow = await createTopLevelWindow(test, OTHER_ORIGIN1); let interestGroup = JSON.stringify( createInterestGroupForOrigin(uuid, window.location.origin)); await runInFrame(test, topLevelWindow, `await joinCrossOriginInterestGroup(test_instance, "${uuid}", "${window.location.origin}", ${interestGroup});`); // Call "clearOriginJoinedAdInterestGroups()", which should not leave the interest // group, since it was joined from a cross-origin main frame. await navigator.clearOriginJoinedAdInterestGroups(window.location.origin); // Make sure group was not left. await runBasicFledgeTestExpectingWinner(test, uuid); }, 'clearOriginJoinedAdInterestGroups(), group joined from cross-origin top-level context.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); await joinInterestGroup(test, uuid); // In a cross-origin iframe, call clearOriginJoinedAdInterestGroups() both for the // iframe's origin and for the main frame's origin. The latter should throw an // exception, and neither should manage to leave the interest group. let iframe = await createIframe(test, OTHER_ORIGIN1, 'join-ad-interest-group'); await runInFrame(test, iframe, `// Call clearOriginJoinedAdInterestGroups() with the iframe's origin. await navigator.clearOriginJoinedAdInterestGroups(window.location.origin); try { // Call clearOriginJoinedAdInterestGroups() with the main frame's origin. await navigator.clearOriginJoinedAdInterestGroups("${window.location.origin}"); } catch (e) { assert_true(e instanceof DOMException, "DOMException thrown"); assert_equals(e.name, "NotAllowedError", "NotAllowedError DOMException thrown"); return {result: "success"}; } throw "Exception unexpectedly not thrown";`); // Confirm that the interest group was not left. await runBasicFledgeTestExpectingWinner(test, uuid); }, "clearOriginJoinedAdInterestGroups(), cross-origin iframe tries to leave parent frame's group."); subsetTest(promise_test, async test => { const uuid = generateUuid(test); // The possible results of calling clearOriginJoinedAdInterestGroups(): // Doesn't throw an exception. const noExpectionURL = createTrackerURL(origin, uuid, "track_get", "no_exception"); // Throws the exception it's expected to. const exceptionURL = createTrackerURL(origin, uuid, "track_get", "exception"); // Throws the wrong exception. const badExpectionURL = createTrackerURL(origin, uuid, "track_get", "bad_exception"); // Create a render URL that calls clearOriginJoinedAdInterestGroups() and // then requests one of the above tracking URLs, based on the resulting // behaviot. const renderURL = createRenderURL( uuid, `async function TryClear() { try { await navigator.clearOriginJoinedAdInterestGroups( "${window.location.origin}"); await fetch("${noExpectionURL}"); } catch (e) { if (e instanceof DOMException && e.name === "NotAllowedError") { await fetch("${exceptionURL}"); } else { await fetch("${badExpectionURL}"); } } } TryClear();`); await joinInterestGroup( test, uuid, {ads: [{ renderURL: renderURL}]}); await runBasicFledgeAuctionAndNavigate(test, uuid); // This should wait until the clear call has thrown an exception. await waitForObservedRequests( uuid, [createBidderReportURL(uuid), createSellerReportURL(uuid), exceptionURL]); // Check the interest group was not left. await runBasicFledgeTestExpectingWinner(test, uuid); }, 'clearOriginJoinedAdInterestGroups() in ad fenced frame throws an exception.'); /////////////////////////////////////////////////////////////////////////////// // Tests where some interest groups are owned by another origin. /////////////////////////////////////////////////////////////////////////////// subsetTest(promise_test, async test => { const uuid = generateUuid(test); // Join interest group in iframe and make sure it was joined. let iframe = await createIframe(test, OTHER_ORIGIN1, 'join-ad-interest-group'); await runInFrame(test, iframe, `await joinInterestGroup(test_instance, "${uuid}"); await runBasicFledgeTestExpectingWinner(test_instance, "${uuid}");`); // In the main frame, Call clearOriginJoinedAdInterestGroups() for both the main // frame's origin, and the origin of the iframe / joined interest group. Neither // should leave the group, and the second should throw. await navigator.clearOriginJoinedAdInterestGroups(window.location.origin); try { await navigator.clearOriginJoinedAdInterestGroups(OTHER_ORIGIN1); throw 'Exception unexpectedly not thrown'; } catch (e) { if (!(e instanceof DOMException) || e.name !== 'NotAllowedError') { throw 'Wrong exception thrown: ' + e.toString(); } } // In an iframe, confirm the group was never left. await runInFrame(test, iframe, `await runBasicFledgeTestExpectingWinner(test_instance, "${uuid}");`); }, 'clearOriginJoinedAdInterestGroups(). Cross-origin interest group joined in iframe, try to clear in main frame.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); let iframe = await createIframe(test, OTHER_ORIGIN1, 'join-ad-interest-group'); await runInFrame(test, iframe, `await joinInterestGroup(test_instance, "${uuid}"); // Confirm that trying to clear the interest group using the main frame's // origin throws, and does not leave the group. try { await navigator.clearOriginJoinedAdInterestGroups("${window.location.origin}"); throw 'Exception unexpectedly not thrown'; } catch (e) { if (!(e instanceof DOMException) || e.name !== 'NotAllowedError') { throw 'Wrong exception thrown: ' + e.toString(); } } await runBasicFledgeTestExpectingWinner(test_instance, "${uuid}");`); }, 'clearOriginJoinedAdInterestGroups(). Cross-origin interest group joined in iframe, clear call in iframe passing main frame origin.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); let iframe = await createIframe(test, OTHER_ORIGIN1, 'join-ad-interest-group'); await runInFrame(test, iframe, `await joinInterestGroup(test_instance, "${uuid}"); // Clear call with the origin of the cross-origin iframe. // This should successfully leave the interest group. await navigator.clearOriginJoinedAdInterestGroups("${OTHER_ORIGIN1}"); // Verify the group was left. await runBasicFledgeTestExpectingNoWinner(test_instance, "${uuid}");`); }, 'clearOriginJoinedAdInterestGroups(). Cross-origin interest group joined in iframe, clear call in iframe passing iframe origin.'); subsetTest(promise_test, async test => { const uuid = generateUuid(test); // Join an OTHER_ORIGIN1 interest group in an OTHER_ORIGIN1 main frame. let topLevelWindow = await createTopLevelWindow(test, OTHER_ORIGIN1); await runInFrame(test, topLevelWindow, `await joinInterestGroup(test_instance, "${uuid}");`); let iframe = await createIframe(test, OTHER_ORIGIN1, 'join-ad-interest-group'); await runInFrame(test, iframe, `// Clear call from an OTHER_ORIGIN1 iframe on a different // origin's main frame. This should not clear the interest // group that was just joined, because the joining origin // does not match. await navigator.clearOriginJoinedAdInterestGroups("${OTHER_ORIGIN1}"); // Verify the group was not left. await runBasicFledgeTestExpectingWinner(test_instance, "${uuid}");`); }, 'clearOriginJoinedAdInterestGroups(). Cross-origin interest group joined from another joining origin, clear call in iframe.');