1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/test/general/browser_sanitizeDialog_treeView.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,632 @@ 1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/** 1.11 + * Tests the sanitize dialog (a.k.a. the clear recent history dialog). 1.12 + * See bug 480169. 1.13 + * 1.14 + * The purpose of this test is not to fully flex the sanitize timespan code; 1.15 + * browser/base/content/test/general/browser_sanitize-timespans.js does that. This 1.16 + * test checks the UI of the dialog and makes sure it's correctly connected to 1.17 + * the sanitize timespan code. 1.18 + * 1.19 + * Some of this code, especially the history creation parts, was taken from 1.20 + * browser/base/content/test/general/browser_sanitize-timespans.js. 1.21 + */ 1.22 + 1.23 +Cc["@mozilla.org/moz/jssubscript-loader;1"]. 1.24 + getService(Ci.mozIJSSubScriptLoader). 1.25 + loadSubScript("chrome://browser/content/sanitize.js"); 1.26 + 1.27 +const dm = Cc["@mozilla.org/download-manager;1"]. 1.28 + getService(Ci.nsIDownloadManager); 1.29 +const formhist = Cc["@mozilla.org/satchel/form-history;1"]. 1.30 + getService(Ci.nsIFormHistory2); 1.31 + 1.32 +// Add tests here. Each is a function that's called by doNextTest(). 1.33 +var gAllTests = [ 1.34 + 1.35 + /** 1.36 + * Moves the grippy around, makes sure it works OK. 1.37 + */ 1.38 + function () { 1.39 + // Add history (within the past hour) to get some rows in the tree. 1.40 + let uris = []; 1.41 + let places = []; 1.42 + let pURI; 1.43 + for (let i = 0; i < 30; i++) { 1.44 + pURI = makeURI("http://" + i + "-minutes-ago.com/"); 1.45 + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); 1.46 + uris.push(pURI); 1.47 + } 1.48 + 1.49 + addVisits(places, function() { 1.50 + // Open the dialog and do our tests. 1.51 + openWindow(function (aWin) { 1.52 + let wh = new WindowHelper(aWin); 1.53 + wh.selectDuration(Sanitizer.TIMESPAN_HOUR); 1.54 + wh.checkGrippy("Grippy should be at last row after selecting HOUR " + 1.55 + "duration", 1.56 + wh.getRowCount() - 1); 1.57 + 1.58 + // Move the grippy around. 1.59 + let row = wh.getGrippyRow(); 1.60 + while (row !== 0) { 1.61 + row--; 1.62 + wh.moveGrippyBy(-1); 1.63 + wh.checkGrippy("Grippy should be moved up one row", row); 1.64 + } 1.65 + wh.moveGrippyBy(-1); 1.66 + wh.checkGrippy("Grippy should remain at first row after trying to move " + 1.67 + "it up", 1.68 + 0); 1.69 + while (row !== wh.getRowCount() - 1) { 1.70 + row++; 1.71 + wh.moveGrippyBy(1); 1.72 + wh.checkGrippy("Grippy should be moved down one row", row); 1.73 + } 1.74 + wh.moveGrippyBy(1); 1.75 + wh.checkGrippy("Grippy should remain at last row after trying to move " + 1.76 + "it down", 1.77 + wh.getRowCount() - 1); 1.78 + 1.79 + // Cancel the dialog, make sure history visits are not cleared. 1.80 + wh.checkPrefCheckbox("history", false); 1.81 + 1.82 + wh.cancelDialog(); 1.83 + yield promiseHistoryClearedState(uris, false); 1.84 + 1.85 + // OK, done, cleanup after ourselves. 1.86 + blankSlate(); 1.87 + yield promiseHistoryClearedState(uris, true); 1.88 + }); 1.89 + }); 1.90 + }, 1.91 + 1.92 + /** 1.93 + * Ensures that the combined history-downloads checkbox clears both history 1.94 + * visits and downloads when checked; the dialog respects simple timespan. 1.95 + */ 1.96 + function () { 1.97 + // Add history (within the past hour). 1.98 + let uris = []; 1.99 + let places = []; 1.100 + let pURI; 1.101 + for (let i = 0; i < 30; i++) { 1.102 + pURI = makeURI("http://" + i + "-minutes-ago.com/"); 1.103 + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); 1.104 + uris.push(pURI); 1.105 + } 1.106 + // Add history (over an hour ago). 1.107 + let olderURIs = []; 1.108 + for (let i = 0; i < 5; i++) { 1.109 + pURI = makeURI("http://" + (60 + i) + "-minutes-ago.com/"); 1.110 + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(60 + i)}); 1.111 + olderURIs.push(pURI); 1.112 + } 1.113 + 1.114 + addVisits(places, function() { 1.115 + // Add downloads (within the past hour). 1.116 + let downloadIDs = []; 1.117 + for (let i = 0; i < 5; i++) { 1.118 + downloadIDs.push(addDownloadWithMinutesAgo(i)); 1.119 + } 1.120 + // Add downloads (over an hour ago). 1.121 + let olderDownloadIDs = []; 1.122 + for (let i = 0; i < 5; i++) { 1.123 + olderDownloadIDs.push(addDownloadWithMinutesAgo(61 + i)); 1.124 + } 1.125 + let totalHistoryVisits = uris.length + olderURIs.length; 1.126 + 1.127 + // Open the dialog and do our tests. 1.128 + openWindow(function (aWin) { 1.129 + let wh = new WindowHelper(aWin); 1.130 + wh.selectDuration(Sanitizer.TIMESPAN_HOUR); 1.131 + wh.checkGrippy("Grippy should be at proper row after selecting HOUR " + 1.132 + "duration", 1.133 + uris.length); 1.134 + 1.135 + // Accept the dialog, make sure history visits and downloads within one 1.136 + // hour are cleared. 1.137 + wh.checkPrefCheckbox("history", true); 1.138 + wh.acceptDialog(); 1.139 + yield promiseHistoryClearedState(uris, true); 1.140 + ensureDownloadsClearedState(downloadIDs, true); 1.141 + 1.142 + // Make sure visits and downloads > 1 hour still exist. 1.143 + yield promiseHistoryClearedState(olderURIs, false); 1.144 + ensureDownloadsClearedState(olderDownloadIDs, false); 1.145 + 1.146 + // OK, done, cleanup after ourselves. 1.147 + blankSlate(); 1.148 + yield promiseHistoryClearedState(olderURIs, true); 1.149 + ensureDownloadsClearedState(olderDownloadIDs, true); 1.150 + }); 1.151 + }); 1.152 + }, 1.153 + 1.154 + /** 1.155 + * Ensures that the combined history-downloads checkbox removes neither 1.156 + * history visits nor downloads when not checked. 1.157 + */ 1.158 + function () { 1.159 + // Add history, downloads, form entries (within the past hour). 1.160 + let uris = []; 1.161 + let places = []; 1.162 + let pURI; 1.163 + for (let i = 0; i < 5; i++) { 1.164 + pURI = makeURI("http://" + i + "-minutes-ago.com/"); 1.165 + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)}); 1.166 + uris.push(pURI); 1.167 + } 1.168 + 1.169 + addVisits(places, function() { 1.170 + let downloadIDs = []; 1.171 + for (let i = 0; i < 5; i++) { 1.172 + downloadIDs.push(addDownloadWithMinutesAgo(i)); 1.173 + } 1.174 + let formEntries = []; 1.175 + for (let i = 0; i < 5; i++) { 1.176 + formEntries.push(addFormEntryWithMinutesAgo(i)); 1.177 + } 1.178 + 1.179 + // Open the dialog and do our tests. 1.180 + openWindow(function (aWin) { 1.181 + let wh = new WindowHelper(aWin); 1.182 + wh.selectDuration(Sanitizer.TIMESPAN_HOUR); 1.183 + wh.checkGrippy("Grippy should be at last row after selecting HOUR " + 1.184 + "duration", 1.185 + wh.getRowCount() - 1); 1.186 + 1.187 + // Remove only form entries, leave history (including downloads). 1.188 + wh.checkPrefCheckbox("history", false); 1.189 + wh.checkPrefCheckbox("formdata", true); 1.190 + wh.acceptDialog(); 1.191 + 1.192 + // Of the three only form entries should be cleared. 1.193 + yield promiseHistoryClearedState(uris, false); 1.194 + ensureDownloadsClearedState(downloadIDs, false); 1.195 + ensureFormEntriesClearedState(formEntries, true); 1.196 + 1.197 + // OK, done, cleanup after ourselves. 1.198 + blankSlate(); 1.199 + yield promiseHistoryClearedState(uris, true); 1.200 + ensureDownloadsClearedState(downloadIDs, true); 1.201 + }); 1.202 + }); 1.203 + }, 1.204 + 1.205 + /** 1.206 + * Ensures that the "Everything" duration option works. 1.207 + */ 1.208 + function () { 1.209 + // Add history. 1.210 + let uris = []; 1.211 + let places = []; 1.212 + let pURI; 1.213 + // within past hour, within past two hours, within past four hours and 1.214 + // outside past four hours 1.215 + [10, 70, 130, 250].forEach(function(aValue) { 1.216 + pURI = makeURI("http://" + aValue + "-minutes-ago.com/"); 1.217 + places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)}); 1.218 + uris.push(pURI); 1.219 + }); 1.220 + addVisits(places, function() { 1.221 + 1.222 + // Open the dialog and do our tests. 1.223 + openWindow(function (aWin) { 1.224 + let wh = new WindowHelper(aWin); 1.225 + wh.selectDuration(Sanitizer.TIMESPAN_EVERYTHING); 1.226 + wh.checkPrefCheckbox("history", true); 1.227 + wh.acceptDialog(); 1.228 + yield promiseHistoryClearedState(uris, true); 1.229 + }); 1.230 + }); 1.231 + } 1.232 +]; 1.233 + 1.234 +// Used as the download database ID for a new download. Incremented for each 1.235 +// new download. See addDownloadWithMinutesAgo(). 1.236 +var gDownloadId = 5555551; 1.237 + 1.238 +// Index in gAllTests of the test currently being run. Incremented for each 1.239 +// test run. See doNextTest(). 1.240 +var gCurrTest = 0; 1.241 + 1.242 +var now_uSec = Date.now() * 1000; 1.243 + 1.244 +/////////////////////////////////////////////////////////////////////////////// 1.245 + 1.246 +/** 1.247 + * This wraps the dialog and provides some convenience methods for interacting 1.248 + * with it. 1.249 + * 1.250 + * A warning: Before you call any function that uses the tree (or any function 1.251 + * that calls a function that uses the tree), you must set a non-everything 1.252 + * duration by calling selectDuration(). The dialog does not initialize the 1.253 + * tree if it does not yet need to be shown. 1.254 + * 1.255 + * @param aWin 1.256 + * The dialog's nsIDOMWindow 1.257 + */ 1.258 +function WindowHelper(aWin) { 1.259 + this.win = aWin; 1.260 +} 1.261 + 1.262 +WindowHelper.prototype = { 1.263 + /** 1.264 + * "Presses" the dialog's OK button. 1.265 + */ 1.266 + acceptDialog: function () { 1.267 + is(this.win.document.documentElement.getButton("accept").disabled, false, 1.268 + "Dialog's OK button should not be disabled"); 1.269 + this.win.document.documentElement.acceptDialog(); 1.270 + }, 1.271 + 1.272 + /** 1.273 + * "Presses" the dialog's Cancel button. 1.274 + */ 1.275 + cancelDialog: function () { 1.276 + this.win.document.documentElement.cancelDialog(); 1.277 + }, 1.278 + 1.279 + /** 1.280 + * Ensures that the grippy row is in the right place, tree selection is OK, 1.281 + * and that the grippy's visible. 1.282 + * 1.283 + * @param aMsg 1.284 + * Passed to is() when checking grippy location 1.285 + * @param aExpectedRow 1.286 + * The row that the grippy should be at 1.287 + */ 1.288 + checkGrippy: function (aMsg, aExpectedRow) { 1.289 + is(this.getGrippyRow(), aExpectedRow, aMsg); 1.290 + this.checkTreeSelection(); 1.291 + this.ensureGrippyIsVisible(); 1.292 + }, 1.293 + 1.294 + /** 1.295 + * (Un)checks a history scope checkbox (browser & download history, 1.296 + * form history, etc.). 1.297 + * 1.298 + * @param aPrefName 1.299 + * The final portion of the checkbox's privacy.cpd.* preference name 1.300 + * @param aCheckState 1.301 + * True if the checkbox should be checked, false otherwise 1.302 + */ 1.303 + checkPrefCheckbox: function (aPrefName, aCheckState) { 1.304 + var pref = "privacy.cpd." + aPrefName; 1.305 + var cb = this.win.document.querySelectorAll( 1.306 + "#itemList > [preference='" + pref + "']"); 1.307 + is(cb.length, 1, "found checkbox for " + pref + " preference"); 1.308 + if (cb[0].checked != aCheckState) 1.309 + cb[0].click(); 1.310 + }, 1.311 + 1.312 + /** 1.313 + * Ensures that the tree selection is appropriate to the grippy row. (A 1.314 + * single, contiguous selection should exist from the first row all the way 1.315 + * to the grippy.) 1.316 + */ 1.317 + checkTreeSelection: function () { 1.318 + let grippyRow = this.getGrippyRow(); 1.319 + let sel = this.getTree().view.selection; 1.320 + if (grippyRow === 0) { 1.321 + is(sel.getRangeCount(), 0, 1.322 + "Grippy row is 0, so no tree selection should exist"); 1.323 + } 1.324 + else { 1.325 + is(sel.getRangeCount(), 1, 1.326 + "Grippy row > 0, so only one tree selection range should exist"); 1.327 + let min = {}; 1.328 + let max = {}; 1.329 + sel.getRangeAt(0, min, max); 1.330 + is(min.value, 0, "Tree selection should start at first row"); 1.331 + is(max.value, grippyRow - 1, 1.332 + "Tree selection should end at row before grippy"); 1.333 + } 1.334 + }, 1.335 + 1.336 + /** 1.337 + * The grippy should always be visible when it's moved directly. This method 1.338 + * ensures that. 1.339 + */ 1.340 + ensureGrippyIsVisible: function () { 1.341 + let tbo = this.getTree().treeBoxObject; 1.342 + let firstVis = tbo.getFirstVisibleRow(); 1.343 + let lastVis = tbo.getLastVisibleRow(); 1.344 + let grippyRow = this.getGrippyRow(); 1.345 + ok(firstVis <= grippyRow && grippyRow <= lastVis, 1.346 + "Grippy row should be visible; this inequality should be true: " + 1.347 + firstVis + " <= " + grippyRow + " <= " + lastVis); 1.348 + }, 1.349 + 1.350 + /** 1.351 + * @return The dialog's duration dropdown 1.352 + */ 1.353 + getDurationDropdown: function () { 1.354 + return this.win.document.getElementById("sanitizeDurationChoice"); 1.355 + }, 1.356 + 1.357 + /** 1.358 + * @return The grippy row index 1.359 + */ 1.360 + getGrippyRow: function () { 1.361 + return this.win.gContiguousSelectionTreeHelper.getGrippyRow(); 1.362 + }, 1.363 + 1.364 + /** 1.365 + * @return The tree's row count (includes the grippy row) 1.366 + */ 1.367 + getRowCount: function () { 1.368 + return this.getTree().view.rowCount; 1.369 + }, 1.370 + 1.371 + /** 1.372 + * @return The tree 1.373 + */ 1.374 + getTree: function () { 1.375 + return this.win.gContiguousSelectionTreeHelper.tree; 1.376 + }, 1.377 + 1.378 + /** 1.379 + * @return True if the "Everything" warning panel is visible (as opposed to 1.380 + * the tree) 1.381 + */ 1.382 + isWarningPanelVisible: function () { 1.383 + return this.win.document.getElementById("durationDeck").selectedIndex == 1; 1.384 + }, 1.385 + 1.386 + /** 1.387 + * @return True if the tree is visible (as opposed to the warning panel) 1.388 + */ 1.389 + isTreeVisible: function () { 1.390 + return this.win.document.getElementById("durationDeck").selectedIndex == 0; 1.391 + }, 1.392 + 1.393 + /** 1.394 + * Moves the grippy one row at a time in the direction and magnitude specified. 1.395 + * If aDelta < 0, moves the grippy up; if aDelta > 0, moves it down. 1.396 + * 1.397 + * @param aDelta 1.398 + * The amount and direction to move 1.399 + */ 1.400 + moveGrippyBy: function (aDelta) { 1.401 + if (aDelta === 0) 1.402 + return; 1.403 + let key = aDelta < 0 ? "UP" : "DOWN"; 1.404 + let abs = Math.abs(aDelta); 1.405 + let treechildren = this.getTree().treeBoxObject.treeBody; 1.406 + treechildren.focus(); 1.407 + for (let i = 0; i < abs; i++) { 1.408 + EventUtils.sendKey(key); 1.409 + } 1.410 + }, 1.411 + 1.412 + /** 1.413 + * Selects a duration in the duration dropdown. 1.414 + * 1.415 + * @param aDurVal 1.416 + * One of the Sanitizer.TIMESPAN_* values 1.417 + */ 1.418 + selectDuration: function (aDurVal) { 1.419 + this.getDurationDropdown().value = aDurVal; 1.420 + if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) { 1.421 + is(this.isTreeVisible(), false, 1.422 + "Tree should not be visible for TIMESPAN_EVERYTHING"); 1.423 + is(this.isWarningPanelVisible(), true, 1.424 + "Warning panel should be visible for TIMESPAN_EVERYTHING"); 1.425 + } 1.426 + else { 1.427 + is(this.isTreeVisible(), true, 1.428 + "Tree should be visible for non-TIMESPAN_EVERYTHING"); 1.429 + is(this.isWarningPanelVisible(), false, 1.430 + "Warning panel should not be visible for non-TIMESPAN_EVERYTHING"); 1.431 + } 1.432 + } 1.433 +}; 1.434 + 1.435 +/** 1.436 + * Adds a download to history. 1.437 + * 1.438 + * @param aMinutesAgo 1.439 + * The download will be downloaded this many minutes ago 1.440 + */ 1.441 +function addDownloadWithMinutesAgo(aMinutesAgo) { 1.442 + let name = "fakefile-" + aMinutesAgo + "-minutes-ago"; 1.443 + let data = { 1.444 + id: gDownloadId, 1.445 + name: name, 1.446 + source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", 1.447 + target: name, 1.448 + startTime: now_uSec - (aMinutesAgo * 60 * 1000000), 1.449 + endTime: now_uSec - ((aMinutesAgo + 1) *60 * 1000000), 1.450 + state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, 1.451 + currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, 1.452 + guid: "a1bcD23eF4g5" 1.453 + }; 1.454 + 1.455 + let db = dm.DBConnection; 1.456 + let stmt = db.createStatement( 1.457 + "INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " + 1.458 + "state, currBytes, maxBytes, preferredAction, autoResume, guid) " + 1.459 + "VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " + 1.460 + ":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)"); 1.461 + try { 1.462 + for (let prop in data) { 1.463 + stmt.params[prop] = data[prop]; 1.464 + } 1.465 + stmt.execute(); 1.466 + } 1.467 + finally { 1.468 + stmt.reset(); 1.469 + } 1.470 + 1.471 + is(downloadExists(gDownloadId), true, 1.472 + "Sanity check: download " + gDownloadId + 1.473 + " should exist after creating it"); 1.474 + 1.475 + return gDownloadId++; 1.476 +} 1.477 + 1.478 +/** 1.479 + * Adds a form entry to history. 1.480 + * 1.481 + * @param aMinutesAgo 1.482 + * The entry will be added this many minutes ago 1.483 + */ 1.484 +function addFormEntryWithMinutesAgo(aMinutesAgo) { 1.485 + let name = aMinutesAgo + "-minutes-ago"; 1.486 + formhist.addEntry(name, "dummy"); 1.487 + 1.488 + // Artifically age the entry to the proper vintage. 1.489 + let db = formhist.DBConnection; 1.490 + let timestamp = now_uSec - (aMinutesAgo * 60 * 1000000); 1.491 + db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " + 1.492 + timestamp + " WHERE fieldname = '" + name + "'"); 1.493 + 1.494 + is(formhist.nameExists(name), true, 1.495 + "Sanity check: form entry " + name + " should exist after creating it"); 1.496 + return name; 1.497 +} 1.498 + 1.499 +/** 1.500 + * Removes all history visits, downloads, and form entries. 1.501 + */ 1.502 +function blankSlate() { 1.503 + PlacesUtils.bhistory.removeAllPages(); 1.504 + dm.cleanUp(); 1.505 + formhist.removeAllEntries(); 1.506 +} 1.507 + 1.508 +/** 1.509 + * Checks to see if the download with the specified ID exists. 1.510 + * 1.511 + * @param aID 1.512 + * The ID of the download to check 1.513 + * @return True if the download exists, false otherwise 1.514 + */ 1.515 +function downloadExists(aID) 1.516 +{ 1.517 + let db = dm.DBConnection; 1.518 + let stmt = db.createStatement( 1.519 + "SELECT * " + 1.520 + "FROM moz_downloads " + 1.521 + "WHERE id = :id" 1.522 + ); 1.523 + stmt.params.id = aID; 1.524 + let rows = stmt.executeStep(); 1.525 + stmt.finalize(); 1.526 + return !!rows; 1.527 +} 1.528 + 1.529 +/** 1.530 + * Runs the next test in the gAllTests array. If all tests have been run, 1.531 + * finishes the entire suite. 1.532 + */ 1.533 +function doNextTest() { 1.534 + if (gAllTests.length <= gCurrTest) { 1.535 + blankSlate(); 1.536 + waitForAsyncUpdates(finish); 1.537 + } 1.538 + else { 1.539 + let ct = gCurrTest; 1.540 + gCurrTest++; 1.541 + gAllTests[ct](); 1.542 + } 1.543 +} 1.544 + 1.545 +/** 1.546 + * Ensures that the specified downloads are either cleared or not. 1.547 + * 1.548 + * @param aDownloadIDs 1.549 + * Array of download database IDs 1.550 + * @param aShouldBeCleared 1.551 + * True if each download should be cleared, false otherwise 1.552 + */ 1.553 +function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) { 1.554 + let niceStr = aShouldBeCleared ? "no longer" : "still"; 1.555 + aDownloadIDs.forEach(function (id) { 1.556 + is(downloadExists(id), !aShouldBeCleared, 1.557 + "download " + id + " should " + niceStr + " exist"); 1.558 + }); 1.559 +} 1.560 + 1.561 +/** 1.562 + * Ensures that the specified form entries are either cleared or not. 1.563 + * 1.564 + * @param aFormEntries 1.565 + * Array of form entry names 1.566 + * @param aShouldBeCleared 1.567 + * True if each form entry should be cleared, false otherwise 1.568 + */ 1.569 +function ensureFormEntriesClearedState(aFormEntries, aShouldBeCleared) { 1.570 + let niceStr = aShouldBeCleared ? "no longer" : "still"; 1.571 + aFormEntries.forEach(function (entry) { 1.572 + is(formhist.nameExists(entry), !aShouldBeCleared, 1.573 + "form entry " + entry + " should " + niceStr + " exist"); 1.574 + }); 1.575 +} 1.576 + 1.577 +/** 1.578 + * Opens the sanitize dialog and runs a callback once it's finished loading. 1.579 + * 1.580 + * @param aOnloadCallback 1.581 + * A function that will be called once the dialog has loaded 1.582 + */ 1.583 +function openWindow(aOnloadCallback) { 1.584 + function windowObserver(aSubject, aTopic, aData) { 1.585 + if (aTopic != "domwindowopened") 1.586 + return; 1.587 + 1.588 + Services.ww.unregisterNotification(windowObserver); 1.589 + let win = aSubject.QueryInterface(Ci.nsIDOMWindow); 1.590 + win.addEventListener("load", function onload(event) { 1.591 + win.removeEventListener("load", onload, false); 1.592 + executeSoon(function () { 1.593 + // Some exceptions that reach here don't reach the test harness, but 1.594 + // ok()/is() do... 1.595 + try { 1.596 + Task.spawn(function() { 1.597 + aOnloadCallback(win); 1.598 + }).then(function() { 1.599 + waitForAsyncUpdates(doNextTest); 1.600 + }); 1.601 + } 1.602 + catch (exc) { 1.603 + win.close(); 1.604 + ok(false, "Unexpected exception: " + exc + "\n" + exc.stack); 1.605 + finish(); 1.606 + } 1.607 + }); 1.608 + }, false); 1.609 + } 1.610 + Services.ww.registerNotification(windowObserver); 1.611 + Services.ww.openWindow(null, 1.612 + "chrome://browser/content/sanitize.xul", 1.613 + "Sanitize", 1.614 + "chrome,titlebar,dialog,centerscreen,modal", 1.615 + null); 1.616 +} 1.617 + 1.618 +/** 1.619 + * Creates a visit time. 1.620 + * 1.621 + * @param aMinutesAgo 1.622 + * The visit will be visited this many minutes ago 1.623 + */ 1.624 +function visitTimeForMinutesAgo(aMinutesAgo) { 1.625 + return now_uSec - (aMinutesAgo * 60 * 1000000); 1.626 +} 1.627 + 1.628 +/////////////////////////////////////////////////////////////////////////////// 1.629 + 1.630 +function test() { 1.631 + blankSlate(); 1.632 + waitForExplicitFinish(); 1.633 + // Kick off all the tests in the gAllTests array. 1.634 + waitForAsyncUpdates(doNextTest); 1.635 +}