|
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /** |
|
8 * Tests the sanitize dialog (a.k.a. the clear recent history dialog). |
|
9 * See bug 480169. |
|
10 * |
|
11 * The purpose of this test is not to fully flex the sanitize timespan code; |
|
12 * browser/base/content/test/general/browser_sanitize-timespans.js does that. This |
|
13 * test checks the UI of the dialog and makes sure it's correctly connected to |
|
14 * the sanitize timespan code. |
|
15 * |
|
16 * Some of this code, especially the history creation parts, was taken from |
|
17 * browser/base/content/test/general/browser_sanitize-timespans.js. |
|
18 */ |
|
19 |
|
20 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
21 |
|
22 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory", |
|
23 "resource://gre/modules/FormHistory.jsm"); |
|
24 XPCOMUtils.defineLazyModuleGetter(this, "Downloads", |
|
25 "resource://gre/modules/Downloads.jsm"); |
|
26 |
|
27 let tempScope = {}; |
|
28 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) |
|
29 .loadSubScript("chrome://browser/content/sanitize.js", tempScope); |
|
30 let Sanitizer = tempScope.Sanitizer; |
|
31 |
|
32 const kMsecPerMin = 60 * 1000; |
|
33 const kUsecPerMin = 60 * 1000000; |
|
34 |
|
35 let formEntries, downloadIDs, olderDownloadIDs; |
|
36 |
|
37 // Add tests here. Each is a function that's called by doNextTest(). |
|
38 var gAllTests = [ |
|
39 |
|
40 /** |
|
41 * Initializes the dialog to its default state. |
|
42 */ |
|
43 function () { |
|
44 let wh = new WindowHelper(); |
|
45 wh.onload = function () { |
|
46 // Select "Last Hour" |
|
47 this.selectDuration(Sanitizer.TIMESPAN_HOUR); |
|
48 // Hide details |
|
49 if (!this.getItemList().collapsed) |
|
50 this.toggleDetails(); |
|
51 this.acceptDialog(); |
|
52 }; |
|
53 wh.open(); |
|
54 }, |
|
55 |
|
56 /** |
|
57 * Cancels the dialog, makes sure history not cleared. |
|
58 */ |
|
59 function () { |
|
60 // Add history (within the past hour) |
|
61 let uris = []; |
|
62 let places = []; |
|
63 let pURI; |
|
64 for (let i = 0; i < 30; i++) { |
|
65 pURI = makeURI("http://" + i + "-minutes-ago.com/"); |
|
66 places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); |
|
67 uris.push(pURI); |
|
68 } |
|
69 |
|
70 addVisits(places, function() { |
|
71 let wh = new WindowHelper(); |
|
72 wh.onload = function () { |
|
73 this.selectDuration(Sanitizer.TIMESPAN_HOUR); |
|
74 this.checkPrefCheckbox("history", false); |
|
75 this.checkDetails(false); |
|
76 |
|
77 // Show details |
|
78 this.toggleDetails(); |
|
79 this.checkDetails(true); |
|
80 |
|
81 // Hide details |
|
82 this.toggleDetails(); |
|
83 this.checkDetails(false); |
|
84 this.cancelDialog(); |
|
85 }; |
|
86 wh.onunload = function () { |
|
87 yield promiseHistoryClearedState(uris, false); |
|
88 yield blankSlate(); |
|
89 yield promiseHistoryClearedState(uris, true); |
|
90 }; |
|
91 wh.open(); |
|
92 }); |
|
93 }, |
|
94 |
|
95 function () { |
|
96 // Add downloads (within the past hour). |
|
97 Task.spawn(function () { |
|
98 downloadIDs = []; |
|
99 for (let i = 0; i < 5; i++) { |
|
100 yield addDownloadWithMinutesAgo(downloadIDs, i); |
|
101 } |
|
102 // Add downloads (over an hour ago). |
|
103 olderDownloadIDs = []; |
|
104 for (let i = 0; i < 5; i++) { |
|
105 yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i); |
|
106 } |
|
107 |
|
108 doNextTest(); |
|
109 }).then(null, Components.utils.reportError); |
|
110 }, |
|
111 |
|
112 /** |
|
113 * Ensures that the combined history-downloads checkbox clears both history |
|
114 * visits and downloads when checked; the dialog respects simple timespan. |
|
115 */ |
|
116 function () { |
|
117 // Add history (within the past hour). |
|
118 let uris = []; |
|
119 let places = []; |
|
120 let pURI; |
|
121 for (let i = 0; i < 30; i++) { |
|
122 pURI = makeURI("http://" + i + "-minutes-ago.com/"); |
|
123 places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); |
|
124 uris.push(pURI); |
|
125 } |
|
126 // Add history (over an hour ago). |
|
127 let olderURIs = []; |
|
128 for (let i = 0; i < 5; i++) { |
|
129 pURI = makeURI("http://" + (61 + i) + "-minutes-ago.com/"); |
|
130 places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(61 + i)}); |
|
131 olderURIs.push(pURI); |
|
132 } |
|
133 |
|
134 addVisits(places, function() { |
|
135 let totalHistoryVisits = uris.length + olderURIs.length; |
|
136 |
|
137 let wh = new WindowHelper(); |
|
138 wh.onload = function () { |
|
139 this.selectDuration(Sanitizer.TIMESPAN_HOUR); |
|
140 this.checkPrefCheckbox("history", true); |
|
141 this.acceptDialog(); |
|
142 }; |
|
143 wh.onunload = function () { |
|
144 intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR, |
|
145 "timeSpan pref should be hour after accepting dialog with " + |
|
146 "hour selected"); |
|
147 boolPrefIs("cpd.history", true, |
|
148 "history pref should be true after accepting dialog with " + |
|
149 "history checkbox checked"); |
|
150 boolPrefIs("cpd.downloads", true, |
|
151 "downloads pref should be true after accepting dialog with " + |
|
152 "history checkbox checked"); |
|
153 |
|
154 // History visits and downloads within one hour should be cleared. |
|
155 yield promiseHistoryClearedState(uris, true); |
|
156 yield ensureDownloadsClearedState(downloadIDs, true); |
|
157 |
|
158 // Visits and downloads > 1 hour should still exist. |
|
159 yield promiseHistoryClearedState(olderURIs, false); |
|
160 yield ensureDownloadsClearedState(olderDownloadIDs, false); |
|
161 |
|
162 // OK, done, cleanup after ourselves. |
|
163 yield blankSlate(); |
|
164 yield promiseHistoryClearedState(olderURIs, true); |
|
165 yield ensureDownloadsClearedState(olderDownloadIDs, true); |
|
166 }; |
|
167 wh.open(); |
|
168 }); |
|
169 }, |
|
170 |
|
171 /** |
|
172 * Add form history entries for the next test. |
|
173 */ |
|
174 function () { |
|
175 formEntries = []; |
|
176 |
|
177 let iter = function() { |
|
178 for (let i = 0; i < 5; i++) { |
|
179 formEntries.push(addFormEntryWithMinutesAgo(iter, i)); |
|
180 yield undefined; |
|
181 } |
|
182 doNextTest(); |
|
183 }(); |
|
184 |
|
185 iter.next(); |
|
186 }, |
|
187 |
|
188 function () { |
|
189 // Add downloads (within the past hour). |
|
190 Task.spawn(function () { |
|
191 downloadIDs = []; |
|
192 for (let i = 0; i < 5; i++) { |
|
193 yield addDownloadWithMinutesAgo(downloadIDs, i); |
|
194 } |
|
195 |
|
196 doNextTest(); |
|
197 }).then(null, Components.utils.reportError); |
|
198 }, |
|
199 |
|
200 /** |
|
201 * Ensures that the combined history-downloads checkbox removes neither |
|
202 * history visits nor downloads when not checked. |
|
203 */ |
|
204 function () { |
|
205 // Add history, downloads, form entries (within the past hour). |
|
206 let uris = []; |
|
207 let places = []; |
|
208 let pURI; |
|
209 for (let i = 0; i < 5; i++) { |
|
210 pURI = makeURI("http://" + i + "-minutes-ago.com/"); |
|
211 places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); |
|
212 uris.push(pURI); |
|
213 } |
|
214 |
|
215 addVisits(places, function() { |
|
216 let wh = new WindowHelper(); |
|
217 wh.onload = function () { |
|
218 is(this.isWarningPanelVisible(), false, |
|
219 "Warning panel should be hidden after previously accepting dialog " + |
|
220 "with a predefined timespan"); |
|
221 this.selectDuration(Sanitizer.TIMESPAN_HOUR); |
|
222 |
|
223 // Remove only form entries, leave history (including downloads). |
|
224 this.checkPrefCheckbox("history", false); |
|
225 this.checkPrefCheckbox("formdata", true); |
|
226 this.acceptDialog(); |
|
227 }; |
|
228 wh.onunload = function () { |
|
229 intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR, |
|
230 "timeSpan pref should be hour after accepting dialog with " + |
|
231 "hour selected"); |
|
232 boolPrefIs("cpd.history", false, |
|
233 "history pref should be false after accepting dialog with " + |
|
234 "history checkbox unchecked"); |
|
235 boolPrefIs("cpd.downloads", false, |
|
236 "downloads pref should be false after accepting dialog with " + |
|
237 "history checkbox unchecked"); |
|
238 |
|
239 // Of the three only form entries should be cleared. |
|
240 yield promiseHistoryClearedState(uris, false); |
|
241 yield ensureDownloadsClearedState(downloadIDs, false); |
|
242 |
|
243 formEntries.forEach(function (entry) { |
|
244 let exists = yield formNameExists(entry); |
|
245 is(exists, false, "form entry " + entry + " should no longer exist"); |
|
246 }); |
|
247 |
|
248 // OK, done, cleanup after ourselves. |
|
249 yield blankSlate(); |
|
250 yield promiseHistoryClearedState(uris, true); |
|
251 yield ensureDownloadsClearedState(downloadIDs, true); |
|
252 }; |
|
253 wh.open(); |
|
254 }); |
|
255 }, |
|
256 |
|
257 /** |
|
258 * Ensures that the "Everything" duration option works. |
|
259 */ |
|
260 function () { |
|
261 // Add history. |
|
262 let uris = []; |
|
263 let places = []; |
|
264 let pURI; |
|
265 // within past hour, within past two hours, within past four hours and |
|
266 // outside past four hours |
|
267 [10, 70, 130, 250].forEach(function(aValue) { |
|
268 pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); |
|
269 places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); |
|
270 uris.push(pURI); |
|
271 }); |
|
272 addVisits(places, function() { |
|
273 let wh = new WindowHelper(); |
|
274 wh.onload = function () { |
|
275 is(this.isWarningPanelVisible(), false, |
|
276 "Warning panel should be hidden after previously accepting dialog " + |
|
277 "with a predefined timespan"); |
|
278 this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); |
|
279 this.checkPrefCheckbox("history", true); |
|
280 this.checkDetails(true); |
|
281 |
|
282 // Hide details |
|
283 this.toggleDetails(); |
|
284 this.checkDetails(false); |
|
285 |
|
286 // Show details |
|
287 this.toggleDetails(); |
|
288 this.checkDetails(true); |
|
289 |
|
290 this.acceptDialog(); |
|
291 }; |
|
292 wh.onunload = function () { |
|
293 intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING, |
|
294 "timeSpan pref should be everything after accepting dialog " + |
|
295 "with everything selected"); |
|
296 |
|
297 yield promiseHistoryClearedState(uris, true); |
|
298 }; |
|
299 wh.open(); |
|
300 }); |
|
301 }, |
|
302 |
|
303 /** |
|
304 * Ensures that the "Everything" warning is visible on dialog open after |
|
305 * the previous test. |
|
306 */ |
|
307 function () { |
|
308 // Add history. |
|
309 let uris = []; |
|
310 let places = []; |
|
311 let pURI; |
|
312 // within past hour, within past two hours, within past four hours and |
|
313 // outside past four hours |
|
314 [10, 70, 130, 250].forEach(function(aValue) { |
|
315 pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); |
|
316 places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); |
|
317 uris.push(pURI); |
|
318 }); |
|
319 addVisits(places, function() { |
|
320 let wh = new WindowHelper(); |
|
321 wh.onload = function () { |
|
322 is(this.isWarningPanelVisible(), true, |
|
323 "Warning panel should be visible after previously accepting dialog " + |
|
324 "with clearing everything"); |
|
325 this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); |
|
326 this.checkPrefCheckbox("history", true); |
|
327 this.acceptDialog(); |
|
328 }; |
|
329 wh.onunload = function () { |
|
330 intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING, |
|
331 "timeSpan pref should be everything after accepting dialog " + |
|
332 "with everything selected"); |
|
333 |
|
334 yield promiseHistoryClearedState(uris, true); |
|
335 }; |
|
336 wh.open(); |
|
337 }); |
|
338 }, |
|
339 |
|
340 /** |
|
341 * Add form history entry for the next test. |
|
342 */ |
|
343 function () { |
|
344 let iter = function() { |
|
345 formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ]; |
|
346 yield undefined; |
|
347 doNextTest(); |
|
348 }(); |
|
349 |
|
350 iter.next(); |
|
351 }, |
|
352 |
|
353 /** |
|
354 * The next three tests checks that when a certain history item cannot be |
|
355 * cleared then the checkbox should be both disabled and unchecked. |
|
356 * In addition, we ensure that this behavior does not modify the preferences. |
|
357 */ |
|
358 function () { |
|
359 // Add history. |
|
360 let pURI = makeURI("http://" + 10 + "-minutes-ago.com/"); |
|
361 addVisits({uri: pURI, visitDate: visitTimeForMinutesAgo(10)}, function() { |
|
362 let uris = [ pURI ]; |
|
363 |
|
364 let wh = new WindowHelper(); |
|
365 wh.onload = function() { |
|
366 // Check that the relevant checkboxes are enabled |
|
367 var cb = this.win.document.querySelectorAll( |
|
368 "#itemList > [preference='privacy.cpd.formdata']"); |
|
369 ok(cb.length == 1 && !cb[0].disabled, "There is formdata, checkbox to " + |
|
370 "clear formdata should be enabled."); |
|
371 |
|
372 var cb = this.win.document.querySelectorAll( |
|
373 "#itemList > [preference='privacy.cpd.history']"); |
|
374 ok(cb.length == 1 && !cb[0].disabled, "There is history, checkbox to " + |
|
375 "clear history should be enabled."); |
|
376 |
|
377 this.checkAllCheckboxes(); |
|
378 this.acceptDialog(); |
|
379 }; |
|
380 wh.onunload = function () { |
|
381 yield promiseHistoryClearedState(uris, true); |
|
382 |
|
383 let exists = yield formNameExists(formEntries[0]); |
|
384 is(exists, false, "form entry " + formEntries[0] + " should no longer exist"); |
|
385 }; |
|
386 wh.open(); |
|
387 }); |
|
388 }, |
|
389 function () { |
|
390 let wh = new WindowHelper(); |
|
391 wh.onload = function() { |
|
392 boolPrefIs("cpd.history", true, |
|
393 "history pref should be true after accepting dialog with " + |
|
394 "history checkbox checked"); |
|
395 boolPrefIs("cpd.formdata", true, |
|
396 "formdata pref should be true after accepting dialog with " + |
|
397 "formdata checkbox checked"); |
|
398 |
|
399 |
|
400 // Even though the formdata pref is true, because there is no history |
|
401 // left to clear, the checkbox will be disabled. |
|
402 var cb = this.win.document.querySelectorAll( |
|
403 "#itemList > [preference='privacy.cpd.formdata']"); |
|
404 ok(cb.length == 1 && cb[0].disabled && !cb[0].checked, |
|
405 "There is no formdata history, checkbox should be disabled and be " + |
|
406 "cleared to reduce user confusion (bug 497664)."); |
|
407 |
|
408 var cb = this.win.document.querySelectorAll( |
|
409 "#itemList > [preference='privacy.cpd.history']"); |
|
410 ok(cb.length == 1 && !cb[0].disabled && cb[0].checked, |
|
411 "There is no history, but history checkbox should always be enabled " + |
|
412 "and will be checked from previous preference."); |
|
413 |
|
414 this.acceptDialog(); |
|
415 } |
|
416 wh.open(); |
|
417 }, |
|
418 |
|
419 /** |
|
420 * Add form history entry for the next test. |
|
421 */ |
|
422 function () { |
|
423 let iter = function() { |
|
424 formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ]; |
|
425 yield undefined; |
|
426 doNextTest(); |
|
427 }(); |
|
428 |
|
429 iter.next(); |
|
430 }, |
|
431 |
|
432 function () { |
|
433 let wh = new WindowHelper(); |
|
434 wh.onload = function() { |
|
435 boolPrefIs("cpd.formdata", true, |
|
436 "formdata pref should persist previous value after accepting " + |
|
437 "dialog where you could not clear formdata."); |
|
438 |
|
439 var cb = this.win.document.querySelectorAll( |
|
440 "#itemList > [preference='privacy.cpd.formdata']"); |
|
441 ok(cb.length == 1 && !cb[0].disabled && cb[0].checked, |
|
442 "There exists formEntries so the checkbox should be in sync with " + |
|
443 "the pref."); |
|
444 |
|
445 this.acceptDialog(); |
|
446 }; |
|
447 wh.onunload = function () { |
|
448 let exists = yield formNameExists(formEntries[0]); |
|
449 is(exists, false, "form entry " + formEntries[0] + " should no longer exist"); |
|
450 }; |
|
451 wh.open(); |
|
452 }, |
|
453 |
|
454 |
|
455 /** |
|
456 * These next six tests together ensure that toggling details persists |
|
457 * across dialog openings. |
|
458 */ |
|
459 function () { |
|
460 let wh = new WindowHelper(); |
|
461 wh.onload = function () { |
|
462 // Check all items and select "Everything" |
|
463 this.checkAllCheckboxes(); |
|
464 this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); |
|
465 |
|
466 // Hide details |
|
467 this.toggleDetails(); |
|
468 this.checkDetails(false); |
|
469 this.acceptDialog(); |
|
470 }; |
|
471 wh.open(); |
|
472 }, |
|
473 function () { |
|
474 let wh = new WindowHelper(); |
|
475 wh.onload = function () { |
|
476 // Details should remain closed because all items are checked. |
|
477 this.checkDetails(false); |
|
478 |
|
479 // Uncheck history. |
|
480 this.checkPrefCheckbox("history", false); |
|
481 this.acceptDialog(); |
|
482 }; |
|
483 wh.open(); |
|
484 }, |
|
485 function () { |
|
486 let wh = new WindowHelper(); |
|
487 wh.onload = function () { |
|
488 // Details should be open because not all items are checked. |
|
489 this.checkDetails(true); |
|
490 |
|
491 // Modify the Site Preferences item state (bug 527820) |
|
492 this.checkAllCheckboxes(); |
|
493 this.checkPrefCheckbox("siteSettings", false); |
|
494 this.acceptDialog(); |
|
495 }; |
|
496 wh.open(); |
|
497 }, |
|
498 function () { |
|
499 let wh = new WindowHelper(); |
|
500 wh.onload = function () { |
|
501 // Details should be open because not all items are checked. |
|
502 this.checkDetails(true); |
|
503 |
|
504 // Hide details |
|
505 this.toggleDetails(); |
|
506 this.checkDetails(false); |
|
507 this.cancelDialog(); |
|
508 }; |
|
509 wh.open(); |
|
510 }, |
|
511 function () { |
|
512 let wh = new WindowHelper(); |
|
513 wh.onload = function () { |
|
514 // Details should be open because not all items are checked. |
|
515 this.checkDetails(true); |
|
516 |
|
517 // Select another duration |
|
518 this.selectDuration(Sanitizer.TIMESPAN_HOUR); |
|
519 // Hide details |
|
520 this.toggleDetails(); |
|
521 this.checkDetails(false); |
|
522 this.acceptDialog(); |
|
523 }; |
|
524 wh.open(); |
|
525 }, |
|
526 function () { |
|
527 let wh = new WindowHelper(); |
|
528 wh.onload = function () { |
|
529 // Details should not be open because "Last Hour" is selected |
|
530 this.checkDetails(false); |
|
531 |
|
532 this.cancelDialog(); |
|
533 }; |
|
534 wh.open(); |
|
535 }, |
|
536 function () { |
|
537 let wh = new WindowHelper(); |
|
538 wh.onload = function () { |
|
539 // Details should have remained closed |
|
540 this.checkDetails(false); |
|
541 |
|
542 // Show details |
|
543 this.toggleDetails(); |
|
544 this.checkDetails(true); |
|
545 this.cancelDialog(); |
|
546 }; |
|
547 wh.open(); |
|
548 }, |
|
549 function () { |
|
550 // Test for offline cache deletion |
|
551 |
|
552 // Prepare stuff, we will work with www.example.com |
|
553 var URL = "http://www.example.com"; |
|
554 |
|
555 var ios = Cc["@mozilla.org/network/io-service;1"] |
|
556 .getService(Ci.nsIIOService); |
|
557 var URI = ios.newURI(URL, null, null); |
|
558 |
|
559 var sm = Cc["@mozilla.org/scriptsecuritymanager;1"] |
|
560 .getService(Ci.nsIScriptSecurityManager); |
|
561 var principal = sm.getNoAppCodebasePrincipal(URI); |
|
562 |
|
563 // Give www.example.com privileges to store offline data |
|
564 var pm = Cc["@mozilla.org/permissionmanager;1"] |
|
565 .getService(Ci.nsIPermissionManager); |
|
566 pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION); |
|
567 pm.addFromPrincipal(principal, "offline-app", Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN); |
|
568 |
|
569 // Store something to the offline cache |
|
570 const nsICache = Components.interfaces.nsICache; |
|
571 var cs = Components.classes["@mozilla.org/network/cache-service;1"] |
|
572 .getService(Components.interfaces.nsICacheService); |
|
573 var session = cs.createSession(URL + "/manifest", nsICache.STORE_OFFLINE, nsICache.STREAM_BASED); |
|
574 |
|
575 // Open the dialog |
|
576 let wh = new WindowHelper(); |
|
577 wh.onload = function () { |
|
578 this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); |
|
579 // Show details |
|
580 this.toggleDetails(); |
|
581 // Clear only offlineApps |
|
582 this.uncheckAllCheckboxes(); |
|
583 this.checkPrefCheckbox("offlineApps", true); |
|
584 this.acceptDialog(); |
|
585 }; |
|
586 wh.onunload = function () { |
|
587 // Check if the cache has been deleted |
|
588 var size = -1; |
|
589 var visitor = { |
|
590 visitDevice: function (deviceID, deviceInfo) |
|
591 { |
|
592 if (deviceID == "offline") |
|
593 size = deviceInfo.totalSize; |
|
594 |
|
595 // Do not enumerate entries |
|
596 return false; |
|
597 }, |
|
598 |
|
599 visitEntry: function (deviceID, entryInfo) |
|
600 { |
|
601 // Do not enumerate entries. |
|
602 return false; |
|
603 } |
|
604 }; |
|
605 cs.visitEntries(visitor); |
|
606 is(size, 0, "offline application cache entries evicted"); |
|
607 }; |
|
608 |
|
609 var cacheListener = { |
|
610 onCacheEntryAvailable: function (entry, access, status) { |
|
611 is(status, Cr.NS_OK); |
|
612 var stream = entry.openOutputStream(0); |
|
613 var content = "content"; |
|
614 stream.write(content, content.length); |
|
615 stream.close(); |
|
616 entry.close(); |
|
617 wh.open(); |
|
618 } |
|
619 }; |
|
620 |
|
621 session.asyncOpenCacheEntry(URL, nsICache.ACCESS_READ_WRITE, cacheListener); |
|
622 }, |
|
623 function () { |
|
624 // Test for offline apps permission deletion |
|
625 |
|
626 // Prepare stuff, we will work with www.example.com |
|
627 var URL = "http://www.example.com"; |
|
628 |
|
629 var ios = Cc["@mozilla.org/network/io-service;1"] |
|
630 .getService(Ci.nsIIOService); |
|
631 var URI = ios.newURI(URL, null, null); |
|
632 |
|
633 var sm = Cc["@mozilla.org/scriptsecuritymanager;1"] |
|
634 .getService(Ci.nsIScriptSecurityManager); |
|
635 var principal = sm.getNoAppCodebasePrincipal(URI); |
|
636 |
|
637 // Open the dialog |
|
638 let wh = new WindowHelper(); |
|
639 wh.onload = function () { |
|
640 this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); |
|
641 // Show details |
|
642 this.toggleDetails(); |
|
643 // Clear only offlineApps |
|
644 this.uncheckAllCheckboxes(); |
|
645 this.checkPrefCheckbox("siteSettings", true); |
|
646 this.acceptDialog(); |
|
647 }; |
|
648 wh.onunload = function () { |
|
649 // Check all has been deleted (privileges, data, cache) |
|
650 var pm = Cc["@mozilla.org/permissionmanager;1"] |
|
651 .getService(Ci.nsIPermissionManager); |
|
652 is(pm.testPermissionFromPrincipal(principal, "offline-app"), 0, "offline-app permissions removed"); |
|
653 }; |
|
654 wh.open(); |
|
655 } |
|
656 ]; |
|
657 |
|
658 // Index in gAllTests of the test currently being run. Incremented for each |
|
659 // test run. See doNextTest(). |
|
660 var gCurrTest = 0; |
|
661 |
|
662 let now_mSec = Date.now(); |
|
663 let now_uSec = now_mSec * 1000; |
|
664 |
|
665 /////////////////////////////////////////////////////////////////////////////// |
|
666 |
|
667 /** |
|
668 * This wraps the dialog and provides some convenience methods for interacting |
|
669 * with it. |
|
670 * |
|
671 * @param aWin |
|
672 * The dialog's nsIDOMWindow |
|
673 */ |
|
674 function WindowHelper(aWin) { |
|
675 this.win = aWin; |
|
676 } |
|
677 |
|
678 WindowHelper.prototype = { |
|
679 /** |
|
680 * "Presses" the dialog's OK button. |
|
681 */ |
|
682 acceptDialog: function () { |
|
683 is(this.win.document.documentElement.getButton("accept").disabled, false, |
|
684 "Dialog's OK button should not be disabled"); |
|
685 this.win.document.documentElement.acceptDialog(); |
|
686 }, |
|
687 |
|
688 /** |
|
689 * "Presses" the dialog's Cancel button. |
|
690 */ |
|
691 cancelDialog: function () { |
|
692 this.win.document.documentElement.cancelDialog(); |
|
693 }, |
|
694 |
|
695 /** |
|
696 * Ensures that the details progressive disclosure button and the item list |
|
697 * hidden by it match up. Also makes sure the height of the dialog is |
|
698 * sufficient for the item list and warning panel. |
|
699 * |
|
700 * @param aShouldBeShown |
|
701 * True if you expect the details to be shown and false if hidden |
|
702 */ |
|
703 checkDetails: function (aShouldBeShown) { |
|
704 let button = this.getDetailsButton(); |
|
705 let list = this.getItemList(); |
|
706 let hidden = list.hidden || list.collapsed; |
|
707 is(hidden, !aShouldBeShown, |
|
708 "Details should be " + (aShouldBeShown ? "shown" : "hidden") + |
|
709 " but were actually " + (hidden ? "hidden" : "shown")); |
|
710 let dir = hidden ? "down" : "up"; |
|
711 is(button.className, "expander-" + dir, |
|
712 "Details button should be " + dir + " because item list is " + |
|
713 (hidden ? "" : "not ") + "hidden"); |
|
714 let height = 0; |
|
715 if (!hidden) { |
|
716 ok(list.boxObject.height > 30, "listbox has sufficient size") |
|
717 height += list.boxObject.height; |
|
718 } |
|
719 if (this.isWarningPanelVisible()) |
|
720 height += this.getWarningPanel().boxObject.height; |
|
721 ok(height < this.win.innerHeight, |
|
722 "Window should be tall enough to fit warning panel and item list"); |
|
723 }, |
|
724 |
|
725 /** |
|
726 * (Un)checks a history scope checkbox (browser & download history, |
|
727 * form history, etc.). |
|
728 * |
|
729 * @param aPrefName |
|
730 * The final portion of the checkbox's privacy.cpd.* preference name |
|
731 * @param aCheckState |
|
732 * True if the checkbox should be checked, false otherwise |
|
733 */ |
|
734 checkPrefCheckbox: function (aPrefName, aCheckState) { |
|
735 var pref = "privacy.cpd." + aPrefName; |
|
736 var cb = this.win.document.querySelectorAll( |
|
737 "#itemList > [preference='" + pref + "']"); |
|
738 is(cb.length, 1, "found checkbox for " + pref + " preference"); |
|
739 if (cb[0].checked != aCheckState) |
|
740 cb[0].click(); |
|
741 }, |
|
742 |
|
743 /** |
|
744 * Makes sure all the checkboxes are checked. |
|
745 */ |
|
746 _checkAllCheckboxesCustom: function (check) { |
|
747 var cb = this.win.document.querySelectorAll("#itemList > [preference]"); |
|
748 ok(cb.length > 1, "found checkboxes for preferences"); |
|
749 for (var i = 0; i < cb.length; ++i) { |
|
750 var pref = this.win.document.getElementById(cb[i].getAttribute("preference")); |
|
751 if (!!pref.value ^ check) |
|
752 cb[i].click(); |
|
753 } |
|
754 }, |
|
755 |
|
756 checkAllCheckboxes: function () { |
|
757 this._checkAllCheckboxesCustom(true); |
|
758 }, |
|
759 |
|
760 uncheckAllCheckboxes: function () { |
|
761 this._checkAllCheckboxesCustom(false); |
|
762 }, |
|
763 |
|
764 /** |
|
765 * @return The details progressive disclosure button |
|
766 */ |
|
767 getDetailsButton: function () { |
|
768 return this.win.document.getElementById("detailsExpander"); |
|
769 }, |
|
770 |
|
771 /** |
|
772 * @return The dialog's duration dropdown |
|
773 */ |
|
774 getDurationDropdown: function () { |
|
775 return this.win.document.getElementById("sanitizeDurationChoice"); |
|
776 }, |
|
777 |
|
778 /** |
|
779 * @return The item list hidden by the details progressive disclosure button |
|
780 */ |
|
781 getItemList: function () { |
|
782 return this.win.document.getElementById("itemList"); |
|
783 }, |
|
784 |
|
785 /** |
|
786 * @return The clear-everything warning box |
|
787 */ |
|
788 getWarningPanel: function () { |
|
789 return this.win.document.getElementById("sanitizeEverythingWarningBox"); |
|
790 }, |
|
791 |
|
792 /** |
|
793 * @return True if the "Everything" warning panel is visible (as opposed to |
|
794 * the tree) |
|
795 */ |
|
796 isWarningPanelVisible: function () { |
|
797 return !this.getWarningPanel().hidden; |
|
798 }, |
|
799 |
|
800 /** |
|
801 * Opens the clear recent history dialog. Before calling this, set |
|
802 * this.onload to a function to execute onload. It should close the dialog |
|
803 * when done so that the tests may continue. Set this.onunload to a function |
|
804 * to execute onunload. this.onunload is optional. If it returns true, the |
|
805 * caller is expected to call waitForAsyncUpdates at some point; if false is |
|
806 * returned, waitForAsyncUpdates is called automatically. |
|
807 */ |
|
808 open: function () { |
|
809 let wh = this; |
|
810 |
|
811 function windowObserver(aSubject, aTopic, aData) { |
|
812 if (aTopic != "domwindowopened") |
|
813 return; |
|
814 |
|
815 Services.ww.unregisterNotification(windowObserver); |
|
816 |
|
817 var loaded = false; |
|
818 let win = aSubject.QueryInterface(Ci.nsIDOMWindow); |
|
819 |
|
820 win.addEventListener("load", function onload(event) { |
|
821 win.removeEventListener("load", onload, false); |
|
822 |
|
823 if (win.name !== "SanitizeDialog") |
|
824 return; |
|
825 |
|
826 wh.win = win; |
|
827 loaded = true; |
|
828 |
|
829 executeSoon(function () { |
|
830 // Some exceptions that reach here don't reach the test harness, but |
|
831 // ok()/is() do... |
|
832 try { |
|
833 wh.onload(); |
|
834 } |
|
835 catch (exc) { |
|
836 win.close(); |
|
837 ok(false, "Unexpected exception: " + exc + "\n" + exc.stack); |
|
838 finish(); |
|
839 } |
|
840 }); |
|
841 }, false); |
|
842 |
|
843 win.addEventListener("unload", function onunload(event) { |
|
844 if (win.name !== "SanitizeDialog") { |
|
845 win.removeEventListener("unload", onunload, false); |
|
846 return; |
|
847 } |
|
848 |
|
849 // Why is unload fired before load? |
|
850 if (!loaded) |
|
851 return; |
|
852 |
|
853 win.removeEventListener("unload", onunload, false); |
|
854 wh.win = win; |
|
855 |
|
856 executeSoon(function () { |
|
857 // Some exceptions that reach here don't reach the test harness, but |
|
858 // ok()/is() do... |
|
859 try { |
|
860 if (wh.onunload) { |
|
861 Task.spawn(wh.onunload).then(function() { |
|
862 waitForAsyncUpdates(doNextTest); |
|
863 }).then(null, Components.utils.reportError); |
|
864 } else { |
|
865 waitForAsyncUpdates(doNextTest); |
|
866 } |
|
867 } |
|
868 catch (exc) { |
|
869 win.close(); |
|
870 ok(false, "Unexpected exception: " + exc + "\n" + exc.stack); |
|
871 finish(); |
|
872 } |
|
873 }); |
|
874 }, false); |
|
875 } |
|
876 Services.ww.registerNotification(windowObserver); |
|
877 Services.ww.openWindow(null, |
|
878 "chrome://browser/content/sanitize.xul", |
|
879 "SanitizeDialog", |
|
880 "chrome,titlebar,dialog,centerscreen,modal", |
|
881 null); |
|
882 }, |
|
883 |
|
884 /** |
|
885 * Selects a duration in the duration dropdown. |
|
886 * |
|
887 * @param aDurVal |
|
888 * One of the Sanitizer.TIMESPAN_* values |
|
889 */ |
|
890 selectDuration: function (aDurVal) { |
|
891 this.getDurationDropdown().value = aDurVal; |
|
892 if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) { |
|
893 is(this.isWarningPanelVisible(), true, |
|
894 "Warning panel should be visible for TIMESPAN_EVERYTHING"); |
|
895 } |
|
896 else { |
|
897 is(this.isWarningPanelVisible(), false, |
|
898 "Warning panel should not be visible for non-TIMESPAN_EVERYTHING"); |
|
899 } |
|
900 }, |
|
901 |
|
902 /** |
|
903 * Toggles the details progressive disclosure button. |
|
904 */ |
|
905 toggleDetails: function () { |
|
906 this.getDetailsButton().click(); |
|
907 } |
|
908 }; |
|
909 |
|
910 /** |
|
911 * Adds a download to history. |
|
912 * |
|
913 * @param aMinutesAgo |
|
914 * The download will be downloaded this many minutes ago |
|
915 */ |
|
916 function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) { |
|
917 let publicList = yield Downloads.getList(Downloads.PUBLIC); |
|
918 |
|
919 let name = "fakefile-" + aMinutesAgo + "-minutes-ago"; |
|
920 let download = yield Downloads.createDownload({ |
|
921 source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", |
|
922 target: name |
|
923 }); |
|
924 download.startTime = new Date(now_mSec - (aMinutesAgo * kMsecPerMin)); |
|
925 download.canceled = true; |
|
926 publicList.add(download); |
|
927 |
|
928 ok((yield downloadExists(name)), |
|
929 "Sanity check: download " + name + |
|
930 " should exist after creating it"); |
|
931 |
|
932 aExpectedPathList.push(name); |
|
933 } |
|
934 |
|
935 /** |
|
936 * Adds a form entry to history. |
|
937 * |
|
938 * @param aMinutesAgo |
|
939 * The entry will be added this many minutes ago |
|
940 */ |
|
941 function addFormEntryWithMinutesAgo(then, aMinutesAgo) { |
|
942 let name = aMinutesAgo + "-minutes-ago"; |
|
943 |
|
944 // Artifically age the entry to the proper vintage. |
|
945 let timestamp = now_uSec - (aMinutesAgo * kUsecPerMin); |
|
946 |
|
947 FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp }, |
|
948 { handleError: function (error) { |
|
949 do_throw("Error occurred updating form history: " + error); |
|
950 }, |
|
951 handleCompletion: function (reason) { then.next(); } |
|
952 }); |
|
953 return name; |
|
954 } |
|
955 |
|
956 /** |
|
957 * Checks if a form entry exists. |
|
958 */ |
|
959 function formNameExists(name) |
|
960 { |
|
961 let deferred = Promise.defer(); |
|
962 |
|
963 let count = 0; |
|
964 FormHistory.count({ fieldname: name }, |
|
965 { handleResult: function (result) count = result, |
|
966 handleError: function (error) { |
|
967 do_throw("Error occurred searching form history: " + error); |
|
968 deferred.reject(error); |
|
969 }, |
|
970 handleCompletion: function (reason) { |
|
971 if (!reason) deferred.resolve(count); |
|
972 } |
|
973 }); |
|
974 |
|
975 return deferred.promise; |
|
976 } |
|
977 |
|
978 /** |
|
979 * Removes all history visits, downloads, and form entries. |
|
980 */ |
|
981 function blankSlate() { |
|
982 PlacesUtils.bhistory.removeAllPages(); |
|
983 |
|
984 // The promise is resolved only when removing both downloads and form history are done. |
|
985 let deferred = Promise.defer(); |
|
986 let formHistoryDone = false, downloadsDone = false; |
|
987 |
|
988 Task.spawn(function deleteAllDownloads() { |
|
989 let publicList = yield Downloads.getList(Downloads.PUBLIC); |
|
990 let downloads = yield publicList.getAll(); |
|
991 for (let download of downloads) { |
|
992 yield publicList.remove(download); |
|
993 yield download.finalize(true); |
|
994 } |
|
995 downloadsDone = true; |
|
996 if (formHistoryDone) { |
|
997 deferred.resolve(); |
|
998 } |
|
999 }).then(null, Components.utils.reportError); |
|
1000 |
|
1001 FormHistory.update({ op: "remove" }, |
|
1002 { handleError: function (error) { |
|
1003 do_throw("Error occurred updating form history: " + error); |
|
1004 deferred.reject(error); |
|
1005 }, |
|
1006 handleCompletion: function (reason) { |
|
1007 if (!reason) { |
|
1008 formHistoryDone = true; |
|
1009 if (downloadsDone) { |
|
1010 deferred.resolve(); |
|
1011 } |
|
1012 } |
|
1013 } |
|
1014 }); |
|
1015 return deferred.promise; |
|
1016 } |
|
1017 |
|
1018 /** |
|
1019 * Ensures that the given pref is the expected value. |
|
1020 * |
|
1021 * @param aPrefName |
|
1022 * The pref's sub-branch under the privacy branch |
|
1023 * @param aExpectedVal |
|
1024 * The pref's expected value |
|
1025 * @param aMsg |
|
1026 * Passed to is() |
|
1027 */ |
|
1028 function boolPrefIs(aPrefName, aExpectedVal, aMsg) { |
|
1029 is(gPrefService.getBoolPref("privacy." + aPrefName), aExpectedVal, aMsg); |
|
1030 } |
|
1031 |
|
1032 /** |
|
1033 * Checks to see if the download with the specified path exists. |
|
1034 * |
|
1035 * @param aPath |
|
1036 * The path of the download to check |
|
1037 * @return True if the download exists, false otherwise |
|
1038 */ |
|
1039 function downloadExists(aPath) |
|
1040 { |
|
1041 return Task.spawn(function() { |
|
1042 let publicList = yield Downloads.getList(Downloads.PUBLIC); |
|
1043 let listArray = yield publicList.getAll(); |
|
1044 throw new Task.Result(listArray.some(i => i.target.path == aPath)); |
|
1045 }); |
|
1046 } |
|
1047 |
|
1048 /** |
|
1049 * Runs the next test in the gAllTests array. If all tests have been run, |
|
1050 * finishes the entire suite. |
|
1051 */ |
|
1052 function doNextTest() { |
|
1053 if (gAllTests.length <= gCurrTest) { |
|
1054 blankSlate(); |
|
1055 waitForAsyncUpdates(finish); |
|
1056 } |
|
1057 else { |
|
1058 let ct = gCurrTest; |
|
1059 gCurrTest++; |
|
1060 gAllTests[ct](); |
|
1061 } |
|
1062 } |
|
1063 |
|
1064 /** |
|
1065 * Ensures that the specified downloads are either cleared or not. |
|
1066 * |
|
1067 * @param aDownloadIDs |
|
1068 * Array of download database IDs |
|
1069 * @param aShouldBeCleared |
|
1070 * True if each download should be cleared, false otherwise |
|
1071 */ |
|
1072 function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) { |
|
1073 let niceStr = aShouldBeCleared ? "no longer" : "still"; |
|
1074 aDownloadIDs.forEach(function (id) { |
|
1075 is((yield downloadExists(id)), !aShouldBeCleared, |
|
1076 "download " + id + " should " + niceStr + " exist"); |
|
1077 }); |
|
1078 } |
|
1079 |
|
1080 /** |
|
1081 * Ensures that the given pref is the expected value. |
|
1082 * |
|
1083 * @param aPrefName |
|
1084 * The pref's sub-branch under the privacy branch |
|
1085 * @param aExpectedVal |
|
1086 * The pref's expected value |
|
1087 * @param aMsg |
|
1088 * Passed to is() |
|
1089 */ |
|
1090 function intPrefIs(aPrefName, aExpectedVal, aMsg) { |
|
1091 is(gPrefService.getIntPref("privacy." + aPrefName), aExpectedVal, aMsg); |
|
1092 } |
|
1093 |
|
1094 /** |
|
1095 * Creates a visit time. |
|
1096 * |
|
1097 * @param aMinutesAgo |
|
1098 * The visit will be visited this many minutes ago |
|
1099 */ |
|
1100 function visitTimeForMinutesAgo(aMinutesAgo) { |
|
1101 return now_uSec - aMinutesAgo * kUsecPerMin; |
|
1102 } |
|
1103 |
|
1104 /////////////////////////////////////////////////////////////////////////////// |
|
1105 |
|
1106 function test() { |
|
1107 requestLongerTimeout(2); |
|
1108 waitForExplicitFinish(); |
|
1109 blankSlate(); |
|
1110 // Kick off all the tests in the gAllTests array. |
|
1111 waitForAsyncUpdates(doNextTest); |
|
1112 } |