<!DOCTYPE html>
<html>
<head>
    <script src="/cookies/resources/cookie-utilities.js"></script>
    <script src="resources/util.js"></script>
</head>
<body onload="setTimeout('runTest()', 0)">
<br>
<div id="output"></div>
<br>
<script>
    testRunner.waitUntilDone();
    testRunner.dumpAsText();

    const httpOnlyCookieName = "http-only-cookie";
    const serverSideCookieName = "server-side-cookie";
    const clientSideCookieName = "client-side-cookie";

    function sortStringArray(a, b) {
        a = a.toLowerCase();
        b = b.toLowerCase();

        return a > b ? 1 : b > a ? -1 : 0;
    }

    function addLinebreakToOutput() {
        let element = document.createElement("br");
        output.appendChild(element);
    }

    function addOutput(message) {
        let element = document.createElement("div");
        element.innerText = message;
        output.appendChild(element);
    }

    function checkCookies(isAfterDeletion) {
        let unsortedTestPassedMessages = [];
        let cookies = internals.getCookies();
        if (!cookies.length && isAfterDeletion)
            addOutput("FAIL: " + "No cookies found.");
        for (let cookie of cookies) {
            switch (cookie.name) {
                case httpOnlyCookieName:
                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " statistics processing: " + (isAfterDeletion ? " " : "") + "HttpOnly cookie exists.");
                    break;
                case serverSideCookieName:
                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " statistics processing: Regular server-side cookie exists.");
                    break;
                case clientSideCookieName:
                    unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " statistics processing: Client-side cookie exists.");
                    break;
            }
        }
        let sortedTestPassedMessages = unsortedTestPassedMessages.sort(sortStringArray);
        for (let testPassedMessage of sortedTestPassedMessages) {
            addOutput(testPassedMessage);
        }
    }

    const dbName = "TestDatabase";

    function createIDBDataStore(callback) {
        let request = indexedDB.open(dbName);
        request.onerror = function() {
            addOutput("Couldn't create indexedDB.");
            finishTest();
        };
        request.onupgradeneeded = function(event) {
            let db = event.target.result;
            let objStore = db.createObjectStore("test", {autoIncrement: true});
            objStore.add("value");
            callback();
        }
    }

    const maxIntervals = 20;

    let intervalCounterIDB;
    let checkIDBCallback;
    let checkIDBIntervalID;
    let semaphoreIDBCheck = false;
    function checkIDBDataStoreExists(isAfterDeletion, callback) {
        let request;
        intervalCounterIDB = 0;
        checkIDBCallback = callback;
        if (!isAfterDeletion) {
            // Check until there is a IDB.
            checkIDBIntervalID = setInterval(function() {
                if (semaphoreIDBCheck)
                    return;
                semaphoreIDBCheck = true;

                if (++intervalCounterIDB >= maxIntervals) {
                    clearInterval(checkIDBIntervalID);
                    addOutput("Before statistics processing: IDB entry does not exist.");
                    semaphoreIDBCheck = false;
                    checkIDBCallback();
                } else {
                    request = indexedDB.open(dbName);
                    request.onerror = function () {
                        clearInterval(checkIDBIntervalID);
                        addOutput("Couldn't open indexedDB.");
                        semaphoreIDBCheck = false;
                        finishTest();
                    };
                    request.onupgradeneeded = function () {
                        // Let the next interval check again.
                        semaphoreIDBCheck = false;
                    };
                    request.onsuccess = function () {
                        clearInterval(checkIDBIntervalID);
                        addOutput("Before statistics processing: IDB entry does exist.");
                        semaphoreIDBCheck = false;
                        checkIDBCallback();
                    };
                }
            }, 200);
        } else {
            // Check until there is no IDB.
            checkIDBIntervalID = setInterval(function () {
                if (semaphoreIDBCheck)
                    return;
                semaphoreIDBCheck = true;

                if (++intervalCounterIDB >= maxIntervals) {
                    clearInterval(checkIDBIntervalID);
                    addOutput("After statistics processing: IDB entry does exist.");
                    semaphoreIDBCheck = false;
                    checkIDBCallback();
                } else {
                    request = indexedDB.open(dbName);
                    request.onerror = function () {
                        clearInterval(checkIDBIntervalID);
                        addOutput("Couldn't open indexedDB.");
                        semaphoreIDBCheck = false;
                        finishTest();
                    };
                    request.onupgradeneeded = function () {
                        clearInterval(checkIDBIntervalID);
                        addOutput("After statistics processing: IDB entry does not exist.");
                        semaphoreIDBCheck = false;
                        finishTest();
                    };
                    request.onsuccess = function () {
                        // Let the next interval check again because deletion might be delayed.
                        semaphoreIDBCheck = false;
                    };
                }
            }, 200);
        }
    }

    let intervalCounterLocalStorage;
    let checkLocalStorageCallback;
    let checkLocalStorageIntervalID;
    const localStorageName = "test";
    const localStorageValue = "value";
    function checkLocalStorageExists(isAfterDeletion, callback) {
        intervalCounterLocalStorage = 0;
        checkLocalStorageCallback = callback;
        if (!isAfterDeletion) {
            // Check until there is LocalStorage.
            checkLocalStorageIntervalID = setInterval(function() {
                if (++intervalCounterLocalStorage >= maxIntervals) {
                    clearInterval(checkLocalStorageIntervalID);
                    let value = localStorage.getItem(localStorageName);
                    addOutput("Before statistics processing: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
                    checkLocalStorageCallback();
                } else if (testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
                    clearInterval(checkLocalStorageIntervalID);
                    let value = localStorage.getItem(localStorageName);
                    addOutput("Before statistics processing: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
                    checkLocalStorageCallback();
                }
            }, 100);
        } else {
            // Check until there is no LocalStorage.
            checkLocalStorageIntervalID = setInterval(function() {
                if (++intervalCounterLocalStorage >= maxIntervals) {
                    clearInterval(checkLocalStorageIntervalID);
                    let value = localStorage.getItem(localStorageName);
                    addOutput("After statistics processing: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
                    checkLocalStorageCallback();
                } else if (!testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
                    clearInterval(checkLocalStorageIntervalID);
                    let value = localStorage.getItem(localStorageName);
                    addOutput("After statistics processing: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
                    checkLocalStorageCallback();
                }
            }, 100);
        }
    }

    async function writeWebsiteDataAndContinue() {
        // Write cookies.
        await fetch("/cookies/resources/set-http-only-cookie.py?cookieName=" + httpOnlyCookieName, { credentials: "same-origin" });
        await fetch("/cookies/resources/setCookies.cgi", { headers: { "X-Set-Cookie": serverSideCookieName + "=1; path=/;" }, credentials: "same-origin" });
        document.cookie = clientSideCookieName + "=1";

        checkCookies(false);

        // Write LocalStorage
        localStorage.setItem(localStorageName, localStorageValue);
        checkLocalStorageExists(false, function() {

            // Write IndexedDB.
            createIDBDataStore(function () {
                checkIDBDataStoreExists(false, function() {
                    addLinebreakToOutput();
                    processWebsiteDataAndContinue();
                });
            });
        });
    }

    function processWebsiteDataAndContinue() {
        testRunner.installStatisticsDidScanDataRecordsCallback(checkWebsiteDataAndContinue);
        testRunner.statisticsProcessStatisticsAndDataRecords();
    }

    function checkWebsiteDataAndContinue() {
        checkCookies(true);
        checkLocalStorageExists(true, function () {
            checkIDBDataStoreExists(true, finishTest);
        });
    }

    const originUnderTest  = "http://127.0.0.1:8000";
    const otherOrigin = "http://localhost:8000"
    const topFrameOrigin = "http://127.0.0.2:8000";
    const timeStamp = Math.round((new Date()).getTime() / 1000);
    async function finishTest() {
        if (!testRunner.isStatisticsHasHadUserInteraction(originUnderTest))
            addOutput("FAIL: " + originUnderTest + " doesn't have user interaction after processing.");
        await resetCookiesITP();
        setEnableFeature(false, function() {
            testRunner.notifyDone();
        });
    }

    // Do not use setEnableFeature here because we want to make sure website data deletion is not skipped
    // by the presence of parameters().isRunningTest.
    function runTest() {
        testRunner.setStatisticsMinimumTimeBetweenDataRecordsRemoval(3600);
        // Merge a statistic with no user interaction to check the -1 case.
        testRunner.setStatisticsMergeStatistic(otherOrigin, topFrameOrigin, "", timeStamp, false, -1, false, false, false, 0, function() {
            testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(true, function () {
                testRunner.setStatisticsExpiredStatistic(originUnderTest,
                    6,     // numberOfOperatingDaysPassed
                    true,  // hasUserInteraction
                    false, // isScheduledForAllButCookieDataRemoval,
                    false, // isPrevalent
                    function() {
                    writeWebsiteDataAndContinue();
                });
            });
        });
    }
</script>
</body>
</html>
