1<!DOCTYPE html>
2<html>
3<head>
4 <script src="/cookies/resources/cookie-utilities.js"></script>
5 <script src="../resources/util.js"></script>
6</head>
7<body onload="setTimeout('runTest()', 0)">
8<div id="description">Check that website data does not get removed after a period of no user interaction if the website is app-bound.</div>
9<br>
10<div id="output"></div>
11<br>
12<script>
13 testRunner.waitUntilDone();
14 testRunner.dumpAsText();
15 testRunner.setUseITPDatabase(true);
16
17 const httpOnlyCookieName = "http-only-cookie";
18 const serverSideCookieName = "server-side-cookie";
19 const clientSideCookieName = "client-side-cookie";
20
21 function sortStringArray(a, b) {
22 a = a.toLowerCase();
23 b = b.toLowerCase();
24
25 return a > b ? 1 : b > a ? -1 : 0;
26 }
27
28 function addLinebreakToOutput() {
29 let element = document.createElement("br");
30 output.appendChild(element);
31 }
32
33 function addOutput(message) {
34 let element = document.createElement("div");
35 element.innerText = message;
36 output.appendChild(element);
37 }
38
39 function checkCookies(isAfterDeletion) {
40 let unsortedTestPassedMessages = [];
41 let cookies = internals.getCookies();
42 if (!cookies.length)
43 addOutput((isAfterDeletion ? "After" : "Before") + " script-accessible deletion: No cookies found.");
44 for (let cookie of cookies) {
45 switch (cookie.name) {
46 case httpOnlyCookieName:
47 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: " + (isAfterDeletion ? " " : "") + "HttpOnly cookie exists.");
48 break;
49 case serverSideCookieName:
50 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Regular server-side cookie exists.");
51 break;
52 case clientSideCookieName:
53 unsortedTestPassedMessages.push((isAfterDeletion ? "After" : "Before") + " deletion: Client-side cookie exists.");
54 break;
55 }
56 }
57 let sortedTestPassedMessages = unsortedTestPassedMessages.sort(sortStringArray);
58 for (let testPassedMessage of sortedTestPassedMessages) {
59 addOutput(testPassedMessage);
60 }
61 }
62
63 const dbName = "TestDatabase";
64
65 function createIDBDataStore(callback) {
66 let request = indexedDB.open(dbName);
67 request.onerror = function() {
68 addOutput("Couldn't create indexedDB.");
69 finishTest();
70 };
71 request.onupgradeneeded = function(event) {
72 let db = event.target.result;
73 let objStore = db.createObjectStore("test", {autoIncrement: true});
74 objStore.add("value");
75 callback();
76 }
77 }
78
79 const maxIntervals = 20;
80
81 let intervalCounterIDB;
82 let checkIDBCallback;
83 let checkIDBIntervalID;
84 let semaphoreIDBCheck = false;
85 function checkIDBDataStoreExists(isAfterDeletion, callback) {
86 let request;
87 intervalCounterIDB = 0;
88 checkIDBCallback = callback;
89 if (!isAfterDeletion) {
90 // Check until there is a IDB.
91 checkIDBIntervalID = setInterval(function() {
92 if (semaphoreIDBCheck)
93 return;
94 semaphoreIDBCheck = true;
95
96 if (++intervalCounterIDB >= maxIntervals) {
97 clearInterval(checkIDBIntervalID);
98 addOutput("Before deletion: IDB entry does not exist.");
99 semaphoreIDBCheck = false;
100 checkIDBCallback();
101 } else {
102 request = indexedDB.open(dbName);
103 request.onerror = function () {
104 clearInterval(checkIDBIntervalID);
105 addOutput("Couldn't open indexedDB.");
106 semaphoreIDBCheck = false;
107 finishTest();
108 };
109 request.onupgradeneeded = function () {
110 // Let the next interval check again.
111 semaphoreIDBCheck = false;
112 };
113 request.onsuccess = function () {
114 clearInterval(checkIDBIntervalID);
115 addOutput("Before deletion: IDB entry does exist.");
116 semaphoreIDBCheck = false;
117 checkIDBCallback();
118 };
119 }
120 }, 200);
121 } else {
122 // Check until there is a IDB.
123 checkIDBIntervalID = setInterval(function () {
124 if (semaphoreIDBCheck)
125 return;
126 semaphoreIDBCheck = true;
127
128 if (++intervalCounterIDB >= maxIntervals) {
129 clearInterval(checkIDBIntervalID);
130 addOutput("After deletion: IDB entry checks exhausted.");
131 semaphoreIDBCheck = false;
132 checkIDBCallback();
133 } else {
134 request = indexedDB.open(dbName);
135 request.onerror = function () {
136 clearInterval(checkIDBIntervalID);
137 addOutput("Couldn't open indexedDB.");
138 semaphoreIDBCheck = false;
139 finishTest();
140 };
141 request.onupgradeneeded = function () {
142 // Let the next interval check again.
143 semaphoreIDBCheck = false;
144 };
145 request.onsuccess = function () {
146 clearInterval(checkIDBIntervalID);
147 addOutput("After deletion: IDB entry does exist.");
148 semaphoreIDBCheck = false;
149 checkIDBCallback();
150 };
151 }
152 }, 200);
153 }
154 }
155
156 let intervalCounterLocalStorage;
157 let checkLocalStorageCallback;
158 let checkLocalStorageIntervalID;
159 const localStorageName = "test";
160 const localStorageValue = "value";
161 function checkLocalStorageExists(isAfterDeletion, callback) {
162 intervalCounterLocalStorage = 0;
163 checkLocalStorageCallback = callback;
164 if (!isAfterDeletion) {
165 // Check until there is LocalStorage.
166 checkLocalStorageIntervalID = setInterval(function() {
167 if (++intervalCounterLocalStorage >= maxIntervals) {
168 clearInterval(checkLocalStorageIntervalID);
169 let value = localStorage.getItem(localStorageName);
170 addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
171 checkLocalStorageCallback();
172 } else if (testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
173 clearInterval(checkLocalStorageIntervalID);
174 let value = localStorage.getItem(localStorageName);
175 addOutput("Before deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
176 checkLocalStorageCallback();
177 }
178 }, 100);
179 } else {
180 // Check until there is no LocalStorage.
181 checkLocalStorageIntervalID = setInterval(function() {
182 if (++intervalCounterLocalStorage >= maxIntervals) {
183 clearInterval(checkLocalStorageIntervalID);
184 let value = localStorage.getItem(localStorageName);
185 addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
186 checkLocalStorageCallback();
187 } else if (!testRunner.isStatisticsHasLocalStorage(originUnderTest)) {
188 clearInterval(checkLocalStorageIntervalID);
189 let value = localStorage.getItem(localStorageName);
190 addOutput("After deletion: LocalStorage entry " + (value === localStorageValue ? "does" : "does not") + " exist.");
191 checkLocalStorageCallback();
192 }
193 }, 100);
194 }
195 }
196
197 async function writeWebsiteDataAndContinue() {
198 // Write cookies.
199 await fetch("/cookies/resources/set-http-only-cookie.php?cookieName=" + httpOnlyCookieName, { credentials: "same-origin" });
200 await fetch("/cookies/resources/setCookies.cgi", { headers: { "Set-Cookie": serverSideCookieName + "=1; path=/;" }, credentials: "same-origin" });
201 document.cookie = clientSideCookieName + "=1";
202
203 checkCookies(false);
204
205 // Write LocalStorage
206 localStorage.setItem(localStorageName, localStorageValue);
207 checkLocalStorageExists(false, function() {
208
209 // Write IndexedDB.
210 createIDBDataStore(function () {
211 checkIDBDataStoreExists(false, function() {
212 addLinebreakToOutput();
213 processWebsiteDataAndContinue();
214 });
215 });
216 });
217 }
218
219 function processWebsiteDataAndContinue() {
220 testRunner.installStatisticsDidScanDataRecordsCallback(checkWebsiteDataAndContinue);
221 testRunner.statisticsProcessStatisticsAndDataRecords();
222 }
223
224 function checkWebsiteDataAndContinue() {
225 checkCookies(true);
226 checkLocalStorageExists(true, function () {
227 checkIDBDataStoreExists(true, finishTest);
228 });
229 }
230
231 function finishTest() {
232 resetCookies();
233 testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(false, function() {
234 setEnableFeature(false, function() {
235 testRunner.notifyDone();
236 });
237 });
238 }
239
240 const originUnderTest = "http://127.0.0.1:8000";
241 function runTest() {
242 setEnableFeature(true, function () {
243 if (1 !== testRunner.addStatisticsAppBoundDomain(originUnderTest))
244 testFailed("Didn't get a total of one app-bound domain after adding the first.");
245
246 testRunner.setStatisticsFirstPartyWebsiteDataRemovalMode(true, function() {
247 testRunner.setStatisticsPrevalentResource(originUnderTest, true, function() {
248 if (!testRunner.isStatisticsPrevalentResource(originUnderTest))
249 addOutput("FAIL: " + originUnderTest + " didn't get classified as prevalent.");
250 writeWebsiteDataAndContinue();
251 });
252 });
253 });
254 }
255</script>
256</body>
257</html>