gecko-dev/browser/base/content/test/general/browser_sanitizeDialog_treeView.js
Jim Blandy 7e20285e70 Bug 914753: Make Emacs file variable header lines correct, or at least consistent. DONTBUILD r=ehsan
The -*- file variable lines -*- establish per-file settings that Emacs will
pick up. This patch makes the following changes to those lines (and touches
nothing else):

 - Never set the buffer's mode.

   Years ago, Emacs did not have a good JavaScript mode, so it made sense
   to use Java or C++ mode in .js files. However, Emacs has had js-mode for
   years now; it's perfectly serviceable, and is available and enabled by
   default in all major Emacs packagings.

   Selecting a mode in the -*- file variable line -*- is almost always the
   wrong thing to do anyway. It overrides Emacs's default choice, which is
   (now) reasonable; and even worse, it overrides settings the user might
   have made in their '.emacs' file for that file extension. It's only
   useful when there's something specific about that particular file that
   makes a particular mode appropriate.

 - Correctly propagate settings that establish the correct indentation
   level for this file: c-basic-offset and js2-basic-offset should be
   js-indent-level. Whatever value they're given should be preserved;
   different parts of our tree use different indentation styles.

 - We don't use tabs in Mozilla JS code. Always set indent-tabs-mode: nil.
   Remove tab-width: settings, at least in files that don't contain tab
   characters.

 - Remove js2-mode settings that belong in the user's .emacs file, like
   js2-skip-preprocessor-directives.
2014-06-24 22:12:07 -07:00

632 lines
19 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Tests the sanitize dialog (a.k.a. the clear recent history dialog).
* See bug 480169.
*
* The purpose of this test is not to fully flex the sanitize timespan code;
* browser/base/content/test/general/browser_sanitize-timespans.js does that. This
* test checks the UI of the dialog and makes sure it's correctly connected to
* the sanitize timespan code.
*
* Some of this code, especially the history creation parts, was taken from
* browser/base/content/test/general/browser_sanitize-timespans.js.
*/
Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader).
loadSubScript("chrome://browser/content/sanitize.js");
const dm = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager);
const formhist = Cc["@mozilla.org/satchel/form-history;1"].
getService(Ci.nsIFormHistory2);
// Add tests here. Each is a function that's called by doNextTest().
var gAllTests = [
/**
* Moves the grippy around, makes sure it works OK.
*/
function () {
// Add history (within the past hour) to get some rows in the tree.
let uris = [];
let places = [];
let pURI;
for (let i = 0; i < 30; i++) {
pURI = makeURI("http://" + i + "-minutes-ago.com/");
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
uris.push(pURI);
}
addVisits(places, function() {
// Open the dialog and do our tests.
openWindow(function (aWin) {
let wh = new WindowHelper(aWin);
wh.selectDuration(Sanitizer.TIMESPAN_HOUR);
wh.checkGrippy("Grippy should be at last row after selecting HOUR " +
"duration",
wh.getRowCount() - 1);
// Move the grippy around.
let row = wh.getGrippyRow();
while (row !== 0) {
row--;
wh.moveGrippyBy(-1);
wh.checkGrippy("Grippy should be moved up one row", row);
}
wh.moveGrippyBy(-1);
wh.checkGrippy("Grippy should remain at first row after trying to move " +
"it up",
0);
while (row !== wh.getRowCount() - 1) {
row++;
wh.moveGrippyBy(1);
wh.checkGrippy("Grippy should be moved down one row", row);
}
wh.moveGrippyBy(1);
wh.checkGrippy("Grippy should remain at last row after trying to move " +
"it down",
wh.getRowCount() - 1);
// Cancel the dialog, make sure history visits are not cleared.
wh.checkPrefCheckbox("history", false);
wh.cancelDialog();
yield promiseHistoryClearedState(uris, false);
// OK, done, cleanup after ourselves.
blankSlate();
yield promiseHistoryClearedState(uris, true);
});
});
},
/**
* Ensures that the combined history-downloads checkbox clears both history
* visits and downloads when checked; the dialog respects simple timespan.
*/
function () {
// Add history (within the past hour).
let uris = [];
let places = [];
let pURI;
for (let i = 0; i < 30; i++) {
pURI = makeURI("http://" + i + "-minutes-ago.com/");
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
uris.push(pURI);
}
// Add history (over an hour ago).
let olderURIs = [];
for (let i = 0; i < 5; i++) {
pURI = makeURI("http://" + (60 + i) + "-minutes-ago.com/");
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(60 + i)});
olderURIs.push(pURI);
}
addVisits(places, function() {
// Add downloads (within the past hour).
let downloadIDs = [];
for (let i = 0; i < 5; i++) {
downloadIDs.push(addDownloadWithMinutesAgo(i));
}
// Add downloads (over an hour ago).
let olderDownloadIDs = [];
for (let i = 0; i < 5; i++) {
olderDownloadIDs.push(addDownloadWithMinutesAgo(61 + i));
}
let totalHistoryVisits = uris.length + olderURIs.length;
// Open the dialog and do our tests.
openWindow(function (aWin) {
let wh = new WindowHelper(aWin);
wh.selectDuration(Sanitizer.TIMESPAN_HOUR);
wh.checkGrippy("Grippy should be at proper row after selecting HOUR " +
"duration",
uris.length);
// Accept the dialog, make sure history visits and downloads within one
// hour are cleared.
wh.checkPrefCheckbox("history", true);
wh.acceptDialog();
yield promiseHistoryClearedState(uris, true);
ensureDownloadsClearedState(downloadIDs, true);
// Make sure visits and downloads > 1 hour still exist.
yield promiseHistoryClearedState(olderURIs, false);
ensureDownloadsClearedState(olderDownloadIDs, false);
// OK, done, cleanup after ourselves.
blankSlate();
yield promiseHistoryClearedState(olderURIs, true);
ensureDownloadsClearedState(olderDownloadIDs, true);
});
});
},
/**
* Ensures that the combined history-downloads checkbox removes neither
* history visits nor downloads when not checked.
*/
function () {
// Add history, downloads, form entries (within the past hour).
let uris = [];
let places = [];
let pURI;
for (let i = 0; i < 5; i++) {
pURI = makeURI("http://" + i + "-minutes-ago.com/");
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
uris.push(pURI);
}
addVisits(places, function() {
let downloadIDs = [];
for (let i = 0; i < 5; i++) {
downloadIDs.push(addDownloadWithMinutesAgo(i));
}
let formEntries = [];
for (let i = 0; i < 5; i++) {
formEntries.push(addFormEntryWithMinutesAgo(i));
}
// Open the dialog and do our tests.
openWindow(function (aWin) {
let wh = new WindowHelper(aWin);
wh.selectDuration(Sanitizer.TIMESPAN_HOUR);
wh.checkGrippy("Grippy should be at last row after selecting HOUR " +
"duration",
wh.getRowCount() - 1);
// Remove only form entries, leave history (including downloads).
wh.checkPrefCheckbox("history", false);
wh.checkPrefCheckbox("formdata", true);
wh.acceptDialog();
// Of the three only form entries should be cleared.
yield promiseHistoryClearedState(uris, false);
ensureDownloadsClearedState(downloadIDs, false);
ensureFormEntriesClearedState(formEntries, true);
// OK, done, cleanup after ourselves.
blankSlate();
yield promiseHistoryClearedState(uris, true);
ensureDownloadsClearedState(downloadIDs, true);
});
});
},
/**
* Ensures that the "Everything" duration option works.
*/
function () {
// Add history.
let uris = [];
let places = [];
let pURI;
// within past hour, within past two hours, within past four hours and
// outside past four hours
[10, 70, 130, 250].forEach(function(aValue) {
pURI = makeURI("http://" + aValue + "-minutes-ago.com/");
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)});
uris.push(pURI);
});
addVisits(places, function() {
// Open the dialog and do our tests.
openWindow(function (aWin) {
let wh = new WindowHelper(aWin);
wh.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
wh.checkPrefCheckbox("history", true);
wh.acceptDialog();
yield promiseHistoryClearedState(uris, true);
});
});
}
];
// Used as the download database ID for a new download. Incremented for each
// new download. See addDownloadWithMinutesAgo().
var gDownloadId = 5555551;
// Index in gAllTests of the test currently being run. Incremented for each
// test run. See doNextTest().
var gCurrTest = 0;
var now_uSec = Date.now() * 1000;
///////////////////////////////////////////////////////////////////////////////
/**
* This wraps the dialog and provides some convenience methods for interacting
* with it.
*
* A warning: Before you call any function that uses the tree (or any function
* that calls a function that uses the tree), you must set a non-everything
* duration by calling selectDuration(). The dialog does not initialize the
* tree if it does not yet need to be shown.
*
* @param aWin
* The dialog's nsIDOMWindow
*/
function WindowHelper(aWin) {
this.win = aWin;
}
WindowHelper.prototype = {
/**
* "Presses" the dialog's OK button.
*/
acceptDialog: function () {
is(this.win.document.documentElement.getButton("accept").disabled, false,
"Dialog's OK button should not be disabled");
this.win.document.documentElement.acceptDialog();
},
/**
* "Presses" the dialog's Cancel button.
*/
cancelDialog: function () {
this.win.document.documentElement.cancelDialog();
},
/**
* Ensures that the grippy row is in the right place, tree selection is OK,
* and that the grippy's visible.
*
* @param aMsg
* Passed to is() when checking grippy location
* @param aExpectedRow
* The row that the grippy should be at
*/
checkGrippy: function (aMsg, aExpectedRow) {
is(this.getGrippyRow(), aExpectedRow, aMsg);
this.checkTreeSelection();
this.ensureGrippyIsVisible();
},
/**
* (Un)checks a history scope checkbox (browser & download history,
* form history, etc.).
*
* @param aPrefName
* The final portion of the checkbox's privacy.cpd.* preference name
* @param aCheckState
* True if the checkbox should be checked, false otherwise
*/
checkPrefCheckbox: function (aPrefName, aCheckState) {
var pref = "privacy.cpd." + aPrefName;
var cb = this.win.document.querySelectorAll(
"#itemList > [preference='" + pref + "']");
is(cb.length, 1, "found checkbox for " + pref + " preference");
if (cb[0].checked != aCheckState)
cb[0].click();
},
/**
* Ensures that the tree selection is appropriate to the grippy row. (A
* single, contiguous selection should exist from the first row all the way
* to the grippy.)
*/
checkTreeSelection: function () {
let grippyRow = this.getGrippyRow();
let sel = this.getTree().view.selection;
if (grippyRow === 0) {
is(sel.getRangeCount(), 0,
"Grippy row is 0, so no tree selection should exist");
}
else {
is(sel.getRangeCount(), 1,
"Grippy row > 0, so only one tree selection range should exist");
let min = {};
let max = {};
sel.getRangeAt(0, min, max);
is(min.value, 0, "Tree selection should start at first row");
is(max.value, grippyRow - 1,
"Tree selection should end at row before grippy");
}
},
/**
* The grippy should always be visible when it's moved directly. This method
* ensures that.
*/
ensureGrippyIsVisible: function () {
let tbo = this.getTree().treeBoxObject;
let firstVis = tbo.getFirstVisibleRow();
let lastVis = tbo.getLastVisibleRow();
let grippyRow = this.getGrippyRow();
ok(firstVis <= grippyRow && grippyRow <= lastVis,
"Grippy row should be visible; this inequality should be true: " +
firstVis + " <= " + grippyRow + " <= " + lastVis);
},
/**
* @return The dialog's duration dropdown
*/
getDurationDropdown: function () {
return this.win.document.getElementById("sanitizeDurationChoice");
},
/**
* @return The grippy row index
*/
getGrippyRow: function () {
return this.win.gContiguousSelectionTreeHelper.getGrippyRow();
},
/**
* @return The tree's row count (includes the grippy row)
*/
getRowCount: function () {
return this.getTree().view.rowCount;
},
/**
* @return The tree
*/
getTree: function () {
return this.win.gContiguousSelectionTreeHelper.tree;
},
/**
* @return True if the "Everything" warning panel is visible (as opposed to
* the tree)
*/
isWarningPanelVisible: function () {
return this.win.document.getElementById("durationDeck").selectedIndex == 1;
},
/**
* @return True if the tree is visible (as opposed to the warning panel)
*/
isTreeVisible: function () {
return this.win.document.getElementById("durationDeck").selectedIndex == 0;
},
/**
* Moves the grippy one row at a time in the direction and magnitude specified.
* If aDelta < 0, moves the grippy up; if aDelta > 0, moves it down.
*
* @param aDelta
* The amount and direction to move
*/
moveGrippyBy: function (aDelta) {
if (aDelta === 0)
return;
let key = aDelta < 0 ? "UP" : "DOWN";
let abs = Math.abs(aDelta);
let treechildren = this.getTree().treeBoxObject.treeBody;
treechildren.focus();
for (let i = 0; i < abs; i++) {
EventUtils.sendKey(key);
}
},
/**
* Selects a duration in the duration dropdown.
*
* @param aDurVal
* One of the Sanitizer.TIMESPAN_* values
*/
selectDuration: function (aDurVal) {
this.getDurationDropdown().value = aDurVal;
if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) {
is(this.isTreeVisible(), false,
"Tree should not be visible for TIMESPAN_EVERYTHING");
is(this.isWarningPanelVisible(), true,
"Warning panel should be visible for TIMESPAN_EVERYTHING");
}
else {
is(this.isTreeVisible(), true,
"Tree should be visible for non-TIMESPAN_EVERYTHING");
is(this.isWarningPanelVisible(), false,
"Warning panel should not be visible for non-TIMESPAN_EVERYTHING");
}
}
};
/**
* Adds a download to history.
*
* @param aMinutesAgo
* The download will be downloaded this many minutes ago
*/
function addDownloadWithMinutesAgo(aMinutesAgo) {
let name = "fakefile-" + aMinutesAgo + "-minutes-ago";
let data = {
id: gDownloadId,
name: name,
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
target: name,
startTime: now_uSec - (aMinutesAgo * 60 * 1000000),
endTime: now_uSec - ((aMinutesAgo + 1) *60 * 1000000),
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "a1bcD23eF4g5"
};
let db = dm.DBConnection;
let stmt = db.createStatement(
"INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
"state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
"VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
try {
for (let prop in data) {
stmt.params[prop] = data[prop];
}
stmt.execute();
}
finally {
stmt.reset();
}
is(downloadExists(gDownloadId), true,
"Sanity check: download " + gDownloadId +
" should exist after creating it");
return gDownloadId++;
}
/**
* Adds a form entry to history.
*
* @param aMinutesAgo
* The entry will be added this many minutes ago
*/
function addFormEntryWithMinutesAgo(aMinutesAgo) {
let name = aMinutesAgo + "-minutes-ago";
formhist.addEntry(name, "dummy");
// Artifically age the entry to the proper vintage.
let db = formhist.DBConnection;
let timestamp = now_uSec - (aMinutesAgo * 60 * 1000000);
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
timestamp + " WHERE fieldname = '" + name + "'");
is(formhist.nameExists(name), true,
"Sanity check: form entry " + name + " should exist after creating it");
return name;
}
/**
* Removes all history visits, downloads, and form entries.
*/
function blankSlate() {
PlacesUtils.bhistory.removeAllPages();
dm.cleanUp();
formhist.removeAllEntries();
}
/**
* Checks to see if the download with the specified ID exists.
*
* @param aID
* The ID of the download to check
* @return True if the download exists, false otherwise
*/
function downloadExists(aID)
{
let db = dm.DBConnection;
let stmt = db.createStatement(
"SELECT * " +
"FROM moz_downloads " +
"WHERE id = :id"
);
stmt.params.id = aID;
let rows = stmt.executeStep();
stmt.finalize();
return !!rows;
}
/**
* Runs the next test in the gAllTests array. If all tests have been run,
* finishes the entire suite.
*/
function doNextTest() {
if (gAllTests.length <= gCurrTest) {
blankSlate();
waitForAsyncUpdates(finish);
}
else {
let ct = gCurrTest;
gCurrTest++;
gAllTests[ct]();
}
}
/**
* Ensures that the specified downloads are either cleared or not.
*
* @param aDownloadIDs
* Array of download database IDs
* @param aShouldBeCleared
* True if each download should be cleared, false otherwise
*/
function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) {
let niceStr = aShouldBeCleared ? "no longer" : "still";
aDownloadIDs.forEach(function (id) {
is(downloadExists(id), !aShouldBeCleared,
"download " + id + " should " + niceStr + " exist");
});
}
/**
* Ensures that the specified form entries are either cleared or not.
*
* @param aFormEntries
* Array of form entry names
* @param aShouldBeCleared
* True if each form entry should be cleared, false otherwise
*/
function ensureFormEntriesClearedState(aFormEntries, aShouldBeCleared) {
let niceStr = aShouldBeCleared ? "no longer" : "still";
aFormEntries.forEach(function (entry) {
is(formhist.nameExists(entry), !aShouldBeCleared,
"form entry " + entry + " should " + niceStr + " exist");
});
}
/**
* Opens the sanitize dialog and runs a callback once it's finished loading.
*
* @param aOnloadCallback
* A function that will be called once the dialog has loaded
*/
function openWindow(aOnloadCallback) {
function windowObserver(aSubject, aTopic, aData) {
if (aTopic != "domwindowopened")
return;
Services.ww.unregisterNotification(windowObserver);
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function onload(event) {
win.removeEventListener("load", onload, false);
executeSoon(function () {
// Some exceptions that reach here don't reach the test harness, but
// ok()/is() do...
try {
Task.spawn(function() {
aOnloadCallback(win);
}).then(function() {
waitForAsyncUpdates(doNextTest);
});
}
catch (exc) {
win.close();
ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
finish();
}
});
}, false);
}
Services.ww.registerNotification(windowObserver);
Services.ww.openWindow(null,
"chrome://browser/content/sanitize.xul",
"Sanitize",
"chrome,titlebar,dialog,centerscreen,modal",
null);
}
/**
* Creates a visit time.
*
* @param aMinutesAgo
* The visit will be visited this many minutes ago
*/
function visitTimeForMinutesAgo(aMinutesAgo) {
return now_uSec - (aMinutesAgo * 60 * 1000000);
}
///////////////////////////////////////////////////////////////////////////////
function test() {
blankSlate();
waitForExplicitFinish();
// Kick off all the tests in the gAllTests array.
waitForAsyncUpdates(doNextTest);
}