toolkit/content/tests/widgets/tree_shared.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/content/tests/widgets/tree_shared.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1393 @@
     1.4 +var columns_simpletree =
     1.5 +[
     1.6 +  { name: "name", label: "Name", key: true, properties: "one two" },
     1.7 +  { name: "address", label: "Address" }
     1.8 +];
     1.9 +
    1.10 +var columns_hiertree =
    1.11 +[
    1.12 +  { name: "name", label: "Name", primary: true, key: true, properties: "one two" },
    1.13 +  { name: "address", label: "Address" },
    1.14 +  { name: "planet", label: "Planet" },
    1.15 +  { name: "gender", label: "Gender", cycler: true }
    1.16 +];
    1.17 +
    1.18 +// XXXndeakin still to add some tests for:
    1.19 +//   cycler columns, checkbox cells, progressmeter cells
    1.20 +
    1.21 +// this test function expects a tree to have 8 rows in it when it isn't
    1.22 +// expanded. The tree should only display four rows at a time. If editable,
    1.23 +// the cell at row 1 and column 0 must be editable, and the cell at row 2 and
    1.24 +// column 1 must not be editable.
    1.25 +function testtag_tree(treeid, treerowinfoid, seltype, columnstype, testid)
    1.26 +{
    1.27 +  // Stop keystrokes that aren't handled by the tree from leaking out and
    1.28 +  // scrolling the main Mochitests window!
    1.29 +  function preventDefault(event) {
    1.30 +    event.preventDefault();
    1.31 +  }
    1.32 +  document.addEventListener("keypress", preventDefault, false);
    1.33 +
    1.34 +  var multiple = (seltype == "multiple");
    1.35 +
    1.36 +  var tree = document.getElementById(treeid);
    1.37 +  var treerowinfo = document.getElementById(treerowinfoid);
    1.38 +  var rowInfo;
    1.39 +  if (testid =="tree view")
    1.40 +    rowInfo = getCustomTreeViewCellInfo();
    1.41 +  else
    1.42 +    rowInfo = convertDOMtoTreeRowInfo(treerowinfo, 0, { value: -1 });
    1.43 +  var columnInfo = (columnstype == "simple") ? columns_simpletree : columns_hiertree;
    1.44 +
    1.45 +  is(tree.view.selection.currentColumn, null, testid + " initial currentColumn");
    1.46 +  is(tree.selType, seltype == "multiple" ? "" : seltype, testid + " seltype");
    1.47 +
    1.48 +  // note: the functions below should be in this order due to changes made in later tests
    1.49 +  
    1.50 +  // select the first column in cell selection mode so that the selection
    1.51 +  // functions can be tested
    1.52 +  if (seltype == "cell")
    1.53 +    tree.view.selection.currentColumn = tree.columns[0];
    1.54 +
    1.55 +  testtag_tree_columns(tree, columnInfo, testid);
    1.56 +  testtag_tree_TreeSelection(tree, testid, multiple);
    1.57 +  testtag_tree_TreeSelection_UI(tree, testid, multiple);
    1.58 +  if (seltype == "cell")
    1.59 +    testtag_tree_TreeSelection_UI_cell(tree, testid, rowInfo);
    1.60 +
    1.61 +  testtag_tree_TreeView(tree, testid, rowInfo);
    1.62 +
    1.63 +  is(tree.editable, false, "tree should not be editable");
    1.64 +  // currently, the editable flag means that tree editing cannot be invoked
    1.65 +  // by the user. However, editing can still be started with a script.
    1.66 +  is(tree.editingRow, -1, testid + " initial editingRow");
    1.67 +  is(tree.editingColumn, null, testid + " initial editingColumn");
    1.68 +
    1.69 +  testtag_tree_UI_editing(tree, testid, rowInfo);
    1.70 +
    1.71 +  is(tree.editable, false, "tree should not be editable after testtag_tree_UI_editing");
    1.72 +  // currently, the editable flag means that tree editing cannot be invoked
    1.73 +  // by the user. However, editing can still be started with a script.
    1.74 +  is(tree.editingRow, -1, testid + " initial editingRow (continued)");
    1.75 +  is(tree.editingColumn, null, testid + " initial editingColumn (continued)");
    1.76 +
    1.77 +  var ecolumn = tree.columns[0];
    1.78 +  ok(!tree.startEditing(1, ecolumn), "non-editable trees shouldn't start editing");
    1.79 +  is(tree.editingRow, -1, testid + " failed startEditing shouldn't set editingRow");
    1.80 +  is(tree.editingColumn, null, testid + " failed startEditing shouldn't set editingColumn");  
    1.81 +  
    1.82 +  tree.editable = true;
    1.83 +
    1.84 +  ok(tree.startEditing(1, ecolumn), "startEditing should have returned true");
    1.85 +  is(tree.editingRow, 1, testid + " startEditing editingRow");
    1.86 +  is(tree.editingColumn, ecolumn, testid + " startEditing editingColumn");
    1.87 +  is(tree.getAttribute("editing"), "true", testid + " startEditing editing attribute");
    1.88 +
    1.89 +  tree.stopEditing(true);
    1.90 +  is(tree.editingRow, -1, testid + " stopEditing editingRow");
    1.91 +  is(tree.editingColumn, null, testid + " stopEditing editingColumn");
    1.92 +  is(tree.hasAttribute("editing"), false, testid + " stopEditing editing attribute");
    1.93 +
    1.94 +  tree.startEditing(-1, ecolumn);
    1.95 +  is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing -1 editingRow");
    1.96 +  tree.startEditing(15, ecolumn);
    1.97 +  is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing 15 editingRow");
    1.98 +  tree.startEditing(1, null);
    1.99 +  is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing null column editingRow");
   1.100 +  tree.startEditing(2, tree.columns[1]);
   1.101 +  is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing non editable cell editingRow");
   1.102 +
   1.103 +  tree.startEditing(1, ecolumn);
   1.104 +  var inputField = tree.inputField;
   1.105 +  is(inputField instanceof Components.interfaces.nsIDOMXULTextBoxElement, true, testid + "inputField");
   1.106 +  inputField.value = "Changed Value";
   1.107 +  tree.stopEditing(true);
   1.108 +  is(tree.view.getCellText(1, ecolumn), "Changed Value", testid + "edit cell accept");
   1.109 +
   1.110 +  // this cell can be edited, but stopEditing(false) means don't accept the change.
   1.111 +  tree.startEditing(1, ecolumn);
   1.112 +  inputField.value = "Second Value";
   1.113 +  tree.stopEditing(false);
   1.114 +  is(tree.view.getCellText(1, ecolumn), "Changed Value", testid + "edit cell no accept");
   1.115 +
   1.116 +  tree.editable = false;
   1.117 +
   1.118 +  // do the sorting tests last as it will cause the rows to rearrange
   1.119 +  // skip them for the custom tree view
   1.120 +  if (testid !="tree view")
   1.121 +    testtag_tree_TreeView_rows_sort(tree, testid, rowInfo);
   1.122 +
   1.123 +  testtag_tree_wheel(tree);
   1.124 +
   1.125 +  document.removeEventListener("keypress", preventDefault, false);
   1.126 +
   1.127 +  SimpleTest.finish();
   1.128 +}
   1.129 +
   1.130 +function testtag_tree_columns(tree, expectedColumns, testid)
   1.131 +{
   1.132 +  testid += " ";
   1.133 +
   1.134 +  var columns = tree.columns;
   1.135 +
   1.136 +  is(columns instanceof TreeColumns, true, testid + "columns is a TreeColumns");
   1.137 +  is(columns.count, expectedColumns.length, testid + "TreeColumns count");
   1.138 +  is(columns.length, expectedColumns.length, testid + "TreeColumns length");
   1.139 +
   1.140 +  var treecols = tree.getElementsByTagName("treecols")[0];
   1.141 +  var treecol = treecols.getElementsByTagName("treecol");
   1.142 +
   1.143 +  var x = 0;
   1.144 +  var primary = null, sorted = null, key = null;
   1.145 +  for (var c = 0; c < expectedColumns.length; c++) {
   1.146 +    var adjtestid = testid + " column " + c + " ";
   1.147 +    var column = columns[c];
   1.148 +    var expectedColumn = expectedColumns[c];
   1.149 +    is(columns.getColumnAt(c), column, adjtestid + "getColumnAt");
   1.150 +    is(columns.getNamedColumn(expectedColumn.name), column, adjtestid + "getNamedColumn");
   1.151 +    is(columns.getColumnFor(treecol[c]), column, adjtestid + "getColumnFor");
   1.152 +    if (expectedColumn.primary)
   1.153 +      primary = column;
   1.154 +    if (expectedColumn.sorted)
   1.155 +      sorted = column;
   1.156 +    if (expectedColumn.key)
   1.157 +      key = column;
   1.158 +
   1.159 +    // XXXndeakin on Windows and Linux, some columns are one pixel to the
   1.160 +    // left of where they should be. Could just be a rounding issue.
   1.161 +    var adj = 1;
   1.162 +    is(column.x + adj >= x, true, adjtestid + "position is after last column " +
   1.163 +       column.x + "," + column.width + "," + x);
   1.164 +    is(column.width > 0, true, adjtestid + "width is greater than 0");
   1.165 +    x = column.x + column.width;
   1.166 +
   1.167 +    // now check the TreeColumn properties
   1.168 +    is(column instanceof TreeColumn, true, adjtestid + "is a TreeColumn");
   1.169 +    is(column.element, treecol[c], adjtestid + "element is treecol");
   1.170 +    is(column.columns, columns, adjtestid + "columns is TreeColumns");
   1.171 +    is(column.id, expectedColumn.name, adjtestid + "name");
   1.172 +    is(column.index, c, adjtestid + "index");
   1.173 +    is(column.primary, primary == column, adjtestid + "column is primary");
   1.174 +
   1.175 +    is(column.cycler, "cycler" in expectedColumn && expectedColumn.cycler,
   1.176 +                  adjtestid + "column is cycler");
   1.177 +    is(column.selectable, true, adjtestid + "column is selectable");
   1.178 +    is(column.editable, "editable" in expectedColumn && expectedColumn.editable,
   1.179 +                  adjtestid + "column is editable");
   1.180 +
   1.181 +    is(column.type, "type" in expectedColumn ? expectedColumn.type : 1, adjtestid + "type");
   1.182 +
   1.183 +    is(column.getPrevious(), c > 0 ? columns[c - 1] : null, adjtestid + "getPrevious");
   1.184 +    is(column.getNext(), c < columns.length - 1 ? columns[c + 1] : null, adjtestid + "getNext");
   1.185 +
   1.186 +    // check the view's getColumnProperties method
   1.187 +    var properties = tree.view.getColumnProperties(column);
   1.188 +    var expectedProperties = expectedColumn.properties;
   1.189 +    is(properties,  expectedProperties ? expectedProperties : "", adjtestid + "getColumnProperties");
   1.190 +  }
   1.191 +
   1.192 +  is(columns.getFirstColumn(), columns[0], testid + "getFirstColumn");
   1.193 +  is(columns.getLastColumn(), columns[columns.length - 1], testid + "getLastColumn");
   1.194 +  is(columns.getPrimaryColumn(), primary, testid + "getPrimaryColumn");
   1.195 +  is(columns.getSortedColumn(), sorted, testid + "getSortedColumn");
   1.196 +  is(columns.getKeyColumn(), key, testid + "getKeyColumn");
   1.197 +
   1.198 +  is(columns.getColumnAt(-1), null, testid + "getColumnAt under");
   1.199 +  is(columns.getColumnAt(columns.length), null, testid + "getColumnAt over");
   1.200 +  is(columns.getNamedColumn(""), null, testid + "getNamedColumn null");
   1.201 +  is(columns.getNamedColumn("unknown"), null, testid + "getNamedColumn unknown");
   1.202 +  is(columns.getColumnFor(null), null, testid + "getColumnFor null");
   1.203 +  is(columns.getColumnFor(tree), null, testid + "getColumnFor other");
   1.204 +}
   1.205 +
   1.206 +function testtag_tree_TreeSelection(tree, testid, multiple)
   1.207 +{
   1.208 +  testid += " selection ";
   1.209 +
   1.210 +  var selection = tree.view.selection;
   1.211 +  is(selection instanceof Components.interfaces.nsITreeSelection, true,
   1.212 +                testid + "selection is a TreeSelection");
   1.213 +  is(selection.single, !multiple, testid + "single");
   1.214 +
   1.215 +  testtag_tree_TreeSelection_State(tree, testid + "initial", -1, []);
   1.216 +  is(selection.shiftSelectPivot, -1, testid + "initial shiftSelectPivot");
   1.217 +
   1.218 +  selection.currentIndex = 2;
   1.219 +  testtag_tree_TreeSelection_State(tree, testid + "set currentIndex", 2, []);
   1.220 +  tree.currentIndex = 3;
   1.221 +  testtag_tree_TreeSelection_State(tree, testid + "set tree.currentIndex", 3, []);
   1.222 +
   1.223 +  // test the select() method, which should deselect all rows and select
   1.224 +  // a single row
   1.225 +  selection.select(1);
   1.226 +  testtag_tree_TreeSelection_State(tree, testid + "select 1", 1, [1]);
   1.227 +  selection.select(3);
   1.228 +  testtag_tree_TreeSelection_State(tree, testid + "select 2", 3, [3]);
   1.229 +  selection.select(3);
   1.230 +  testtag_tree_TreeSelection_State(tree, testid + "select same", 3, [3]);
   1.231 +
   1.232 +  selection.currentIndex = 1;
   1.233 +  testtag_tree_TreeSelection_State(tree, testid + "set currentIndex with single selection", 1, [3]);
   1.234 +
   1.235 +  tree.currentIndex = 2;
   1.236 +  testtag_tree_TreeSelection_State(tree, testid + "set tree.currentIndex with single selection", 2, [3]);
   1.237 +
   1.238 +  // check the toggleSelect method. In single selection mode, it only toggles on when
   1.239 +  // there isn't currently a selection.
   1.240 +  selection.toggleSelect(2);
   1.241 +  testtag_tree_TreeSelection_State(tree, testid + "toggleSelect 1", 2, multiple ? [2, 3] : [3]);
   1.242 +  selection.toggleSelect(2);
   1.243 +  selection.toggleSelect(3);
   1.244 +  testtag_tree_TreeSelection_State(tree, testid + "toggleSelect 2", 3, []);
   1.245 +
   1.246 +  // the current index doesn't change after a selectAll, so it should still be set to 1
   1.247 +  // selectAll has no effect on single selection trees
   1.248 +  selection.currentIndex = 1;
   1.249 +  selection.selectAll();
   1.250 +  testtag_tree_TreeSelection_State(tree, testid + "selectAll 1", 1, multiple ? [0, 1, 2, 3, 4, 5, 6 , 7] : []);
   1.251 +  selection.toggleSelect(2);
   1.252 +  testtag_tree_TreeSelection_State(tree, testid + "toggleSelect after selectAll", 2,
   1.253 +                                   multiple ? [0, 1, 3, 4, 5, 6, 7] : [2]);
   1.254 +  selection.clearSelection();
   1.255 +  testtag_tree_TreeSelection_State(tree, testid + "clearSelection", 2, []);
   1.256 +  selection.toggleSelect(3);
   1.257 +  selection.toggleSelect(1);
   1.258 +  if (multiple) {
   1.259 +    selection.selectAll();
   1.260 +    testtag_tree_TreeSelection_State(tree, testid + "selectAll 2", 1, [0, 1, 2, 3, 4, 5, 6, 7]);
   1.261 +  }
   1.262 +  selection.currentIndex = 2;
   1.263 +  selection.clearSelection();
   1.264 +  testtag_tree_TreeSelection_State(tree, testid + "clearSelection after selectAll", 2, []);
   1.265 +
   1.266 +  // XXXndeakin invertSelection isn't implemented
   1.267 +  //  selection.invertSelection();
   1.268 +
   1.269 +  is(selection.shiftSelectPivot, -1, testid + "shiftSelectPivot set to -1");
   1.270 +
   1.271 +  // rangedSelect and clearRange set the currentIndex to the endIndex. The
   1.272 +  // shiftSelectPivot property will be set to startIndex.
   1.273 +  selection.rangedSelect(1, 3, false);
   1.274 +  testtag_tree_TreeSelection_State(tree, testid + "rangedSelect no augment",
   1.275 +                                   multiple ? 3 : 2, multiple ? [1, 2, 3] : []);
   1.276 +  is(selection.shiftSelectPivot, multiple ? 1 : -1,
   1.277 +                testid + "shiftSelectPivot after rangedSelect no augment");
   1.278 +  if (multiple) {
   1.279 +    selection.select(1);
   1.280 +    selection.rangedSelect(0, 2, true);
   1.281 +    testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment", 2, [0, 1, 2]);
   1.282 +    is(selection.shiftSelectPivot, 0, testid + "shiftSelectPivot after rangedSelect augment");
   1.283 +
   1.284 +    selection.clearRange(1, 3);
   1.285 +    testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment", 3, [0]);
   1.286 +
   1.287 +    // check that rangedSelect can take a start value higher than end
   1.288 +    selection.rangedSelect(3, 1, false);
   1.289 +    testtag_tree_TreeSelection_State(tree, testid + "rangedSelect reverse", 1, [1, 2, 3]);
   1.290 +    is(selection.shiftSelectPivot, 3, testid + "shiftSelectPivot after rangedSelect reverse");
   1.291 +
   1.292 +    // check that setting the current index doesn't change the selection
   1.293 +    selection.currentIndex = 0;
   1.294 +    testtag_tree_TreeSelection_State(tree, testid + "currentIndex with range selection", 0, [1, 2, 3]);
   1.295 +  }
   1.296 +
   1.297 +  // both values of rangedSelect may be the same
   1.298 +  selection.rangedSelect(2, 2, false);
   1.299 +  testtag_tree_TreeSelection_State(tree, testid + "rangedSelect one row", 2, [2]);
   1.300 +  is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot after selecting one row");
   1.301 +
   1.302 +  if (multiple) {
   1.303 +    selection.rangedSelect(2, 3, true);
   1.304 +
   1.305 +    // a start index of -1 means from the last point
   1.306 +    selection.rangedSelect(-1, 0, true);
   1.307 +    testtag_tree_TreeSelection_State(tree, testid + "rangedSelect -1 existing selection", 0, [0, 1, 2, 3]);
   1.308 +    is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot after -1 existing selection");
   1.309 +
   1.310 +    selection.currentIndex = 2;
   1.311 +    selection.rangedSelect(-1, 0, false);
   1.312 +    testtag_tree_TreeSelection_State(tree, testid + "rangedSelect -1 from currentIndex", 0, [0, 1, 2]);
   1.313 +    is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot -1 from currentIndex");
   1.314 +  }
   1.315 +
   1.316 +  // XXXndeakin need to test out of range values but these don't work properly
   1.317 +/*
   1.318 +  selection.select(-1);
   1.319 +  testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment -1", -1, []);
   1.320 +
   1.321 +  selection.select(8);
   1.322 +  testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment 8", 3, [0]);
   1.323 +*/
   1.324 +}
   1.325 +
   1.326 +function testtag_tree_TreeSelection_UI(tree, testid, multiple)
   1.327 +{
   1.328 +  testid += " selection UI ";
   1.329 +
   1.330 +  var selection = tree.view.selection;
   1.331 +  selection.clearSelection();
   1.332 +  selection.currentIndex = 0;
   1.333 +  tree.focus();
   1.334 +
   1.335 +  var keydownFired = 0;
   1.336 +  var keypressFired = 0;
   1.337 +  function keydownListener(event)
   1.338 +  {
   1.339 +    keydownFired++;
   1.340 +  }
   1.341 +  function keypressListener(event) {
   1.342 +    keypressFired++;
   1.343 +  }
   1.344 +
   1.345 +  // check that cursor up and down keys navigate up and down
   1.346 +  // select event fires after a delay so don't expect it. The reason it fires after a delay
   1.347 +  // is so that cursor navigation allows quicking skimming over a set of items without
   1.348 +  // actually firing events in-between, improving performance. The select event will only
   1.349 +  // be fired on the row where the cursor stops.
   1.350 +  window.addEventListener("keydown", keydownListener, false);
   1.351 +  window.addEventListener("keypress", keypressListener, false);
   1.352 +
   1.353 +  synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down");
   1.354 +  testtag_tree_TreeSelection_State(tree, testid + "key down", 1, [1], 0);
   1.355 +
   1.356 +  synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up");
   1.357 +  testtag_tree_TreeSelection_State(tree, testid + "key up", 0, [0], 0);
   1.358 +
   1.359 +  synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up at start");
   1.360 +  testtag_tree_TreeSelection_State(tree, testid + "key up at start", 0, [0], 0);
   1.361 +
   1.362 +  // pressing down while the last row is selected should not fire a select event,
   1.363 +  // as the selection won't have changed. Also the view is not scrolled in this case.
   1.364 +  selection.select(7);
   1.365 +  synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down at end");
   1.366 +  testtag_tree_TreeSelection_State(tree, testid + "key down at end", 7, [7], 0);
   1.367 +
   1.368 +  // pressing keys while at the edge of the visible rows should scroll the list
   1.369 +  tree.treeBoxObject.scrollToRow(4);
   1.370 +  selection.select(4);
   1.371 +  synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up with scroll");
   1.372 +  is(tree.treeBoxObject.getFirstVisibleRow(), 3, testid + "key up with scroll");
   1.373 +
   1.374 +  tree.treeBoxObject.scrollToRow(0);
   1.375 +  selection.select(3);
   1.376 +  synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down with scroll");
   1.377 +  is(tree.treeBoxObject.getFirstVisibleRow(), 1, testid + "key down with scroll");
   1.378 +  
   1.379 +  // accel key and cursor movement adjust currentIndex but should not change
   1.380 +  // the selection. In single selection mode, the selection will not change,
   1.381 +  // but instead will just scroll up or down a line
   1.382 +  tree.treeBoxObject.scrollToRow(0);
   1.383 +  selection.select(1);
   1.384 +  synthesizeKeyExpectEvent("VK_DOWN", { accelKey: true }, tree, "!select", "key down with accel");
   1.385 +  testtag_tree_TreeSelection_State(tree, testid + "key down with accel", multiple ? 2 : 1, [1]);
   1.386 +  if (!multiple)
   1.387 +    is(tree.treeBoxObject.getFirstVisibleRow(), 1, testid + "key down with accel and scroll");
   1.388 +
   1.389 +  tree.treeBoxObject.scrollToRow(4);
   1.390 +  selection.select(4);
   1.391 +  synthesizeKeyExpectEvent("VK_UP", { accelKey: true }, tree, "!select", "key up with accel");
   1.392 +  testtag_tree_TreeSelection_State(tree, testid + "key up with accel", multiple ? 3 : 4, [4]);
   1.393 +  if (!multiple)
   1.394 +    is(tree.treeBoxObject.getFirstVisibleRow(), 3, testid + "key up with accel and scroll");
   1.395 +
   1.396 +  // do this three times, one for each state of pageUpOrDownMovesSelection,
   1.397 +  // and then once with the accel key pressed
   1.398 +  for (var t = 0; t < 3; t++) {
   1.399 +    var testidmod = (t == 2) ? " with accel" : (t == 1) ? " rev" : "";
   1.400 +    var keymod = (t == 2) ? { accelKey: true } : { };
   1.401 +
   1.402 +    var moveselection = tree.pageUpOrDownMovesSelection;
   1.403 +    if (t == 2)
   1.404 +      moveselection = !moveselection;
   1.405 +
   1.406 +    tree.treeBoxObject.scrollToRow(4);
   1.407 +    selection.currentIndex = 6;
   1.408 +    selection.select(6);
   1.409 +    var expected = moveselection ? 4 : 6;
   1.410 +    synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up");
   1.411 +    testtag_tree_TreeSelection_State(tree, testid + "key page up" + testidmod,
   1.412 +                                     expected, [expected], moveselection ? 4 : 0);
   1.413 +
   1.414 +    expected = moveselection ? 0 : 6;
   1.415 +    synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up again");
   1.416 +    testtag_tree_TreeSelection_State(tree, testid + "key page up again" + testidmod,
   1.417 +                                     expected, [expected], 0);
   1.418 +
   1.419 +    expected = moveselection ? 0 : 6;
   1.420 +    synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up at start");
   1.421 +    testtag_tree_TreeSelection_State(tree, testid + "key page up at start" + testidmod,
   1.422 +                                     expected, [expected], 0);
   1.423 +
   1.424 +    tree.treeBoxObject.scrollToRow(0);
   1.425 +    selection.currentIndex = 1;
   1.426 +    selection.select(1);
   1.427 +    expected = moveselection ? 3 : 1;
   1.428 +    synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down");
   1.429 +    testtag_tree_TreeSelection_State(tree, testid + "key page down" + testidmod,
   1.430 +                                     expected, [expected], moveselection ? 0 : 4);
   1.431 +
   1.432 +    expected = moveselection ? 7 : 1;
   1.433 +    synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down again");
   1.434 +    testtag_tree_TreeSelection_State(tree, testid + "key page down again" + testidmod,
   1.435 +                                     expected, [expected], 4);
   1.436 +
   1.437 +    expected = moveselection ? 7 : 1;
   1.438 +    synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down at start");
   1.439 +    testtag_tree_TreeSelection_State(tree, testid + "key page down at start" + testidmod,
   1.440 +                                     expected, [expected], 4);
   1.441 +
   1.442 +    if (t < 2)
   1.443 +      tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
   1.444 +  }
   1.445 +
   1.446 +  tree.treeBoxObject.scrollToRow(4);
   1.447 +  selection.select(6);
   1.448 +  synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home");
   1.449 +  testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0);
   1.450 +
   1.451 +  tree.treeBoxObject.scrollToRow(0);
   1.452 +  selection.select(1);
   1.453 +  synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end");
   1.454 +  testtag_tree_TreeSelection_State(tree, testid + "key end", 7, [7], 4);
   1.455 +
   1.456 +  // in single selection mode, the selection doesn't change in this case
   1.457 +  tree.treeBoxObject.scrollToRow(4);
   1.458 +  selection.select(6);
   1.459 +  synthesizeKeyExpectEvent("VK_HOME", { accelKey: true }, tree, "!select", "key home with accel");
   1.460 +  testtag_tree_TreeSelection_State(tree, testid + "key home with accel", multiple ? 0 : 6, [6], 0);
   1.461 +
   1.462 +  tree.treeBoxObject.scrollToRow(0);
   1.463 +  selection.select(1);
   1.464 +  synthesizeKeyExpectEvent("VK_END", { accelKey: true }, tree, "!select", "key end with accel");
   1.465 +  testtag_tree_TreeSelection_State(tree, testid + "key end with accel", multiple ? 7 : 1, [1], 4);
   1.466 +
   1.467 +  // next, test cursor navigation with selection. Here the select event will be fired
   1.468 +  selection.select(1);
   1.469 +  var eventExpected = multiple ? "select" : "!select";
   1.470 +  synthesizeKeyExpectEvent("VK_DOWN", { shiftKey: true }, tree, eventExpected, "key shift down to select");
   1.471 +  testtag_tree_TreeSelection_State(tree, testid + "key shift down to select",
   1.472 +                                   multiple ? 2 : 1, multiple ? [1, 2] : [1]);
   1.473 +  is(selection.shiftSelectPivot, multiple ? 1 : -1,
   1.474 +                testid + "key shift down to select shiftSelectPivot");
   1.475 +  synthesizeKeyExpectEvent("VK_UP", { shiftKey: true }, tree, eventExpected, "key shift up to unselect");
   1.476 +  testtag_tree_TreeSelection_State(tree, testid + "key shift up to unselect", 1, [1]);
   1.477 +  is(selection.shiftSelectPivot, multiple ? 1 : -1,
   1.478 +                testid + "key shift up to unselect shiftSelectPivot");
   1.479 +  if (multiple) {
   1.480 +    synthesizeKeyExpectEvent("VK_UP", { shiftKey: true }, tree, "select", "key shift up to select");
   1.481 +    testtag_tree_TreeSelection_State(tree, testid + "key shift up to select", 0, [0, 1]);
   1.482 +    is(selection.shiftSelectPivot, 1, testid + "key shift up to select shiftSelectPivot");
   1.483 +    synthesizeKeyExpectEvent("VK_DOWN", { shiftKey: true }, tree, "select", "key shift down to unselect");
   1.484 +    testtag_tree_TreeSelection_State(tree, testid + "key shift down to unselect", 1, [1]);
   1.485 +    is(selection.shiftSelectPivot, 1, testid + "key shift down to unselect shiftSelectPivot");
   1.486 +  }
   1.487 +
   1.488 +  // do this twice, one for each state of pageUpOrDownMovesSelection, however
   1.489 +  // when selecting with the shift key, pageUpOrDownMovesSelection is ignored
   1.490 +  // and the selection always changes
   1.491 +  var lastidx = tree.view.rowCount - 1;
   1.492 +  for (var t = 0; t < 2; t++) {
   1.493 +    var testidmod = (t == 0) ? "" : " rev";
   1.494 +
   1.495 +    // If the top or bottom visible row is the current row, pressing shift and
   1.496 +    // page down / page up selects one page up or one page down. Otherwise, the
   1.497 +    // selection is made to the top or bottom of the visible area.
   1.498 +    tree.treeBoxObject.scrollToRow(lastidx - 3);
   1.499 +    selection.currentIndex = 6;
   1.500 +    selection.select(6);
   1.501 +    synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, eventExpected, "key shift page up");
   1.502 +    testtag_tree_TreeSelection_State(tree, testid + "key shift page up" + testidmod,
   1.503 +                                     multiple ? 4 : 6, multiple ? [4, 5, 6] : [6]);
   1.504 +    if (multiple) {
   1.505 +      synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "select", "key shift page up again");
   1.506 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page up again" + testidmod,
   1.507 +                                       0, [0, 1, 2, 3, 4, 5, 6]);
   1.508 +      // no change in the selection, so no select event should be fired
   1.509 +      synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "!select", "key shift page up at start");
   1.510 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page up at start" + testidmod,
   1.511 +                                       0, [0, 1, 2, 3, 4, 5, 6]);
   1.512 +      // deselect by paging down again
   1.513 +      synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "select", "key shift page down deselect");
   1.514 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page down deselect" + testidmod,
   1.515 +                                       3, [3, 4, 5, 6]);
   1.516 +    }
   1.517 +
   1.518 +    tree.treeBoxObject.scrollToRow(1);
   1.519 +    selection.currentIndex = 2;
   1.520 +    selection.select(2);
   1.521 +    synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, eventExpected, "key shift page down");
   1.522 +    testtag_tree_TreeSelection_State(tree, testid + "key shift page down" + testidmod,
   1.523 +                                     multiple ? 4 : 2, multiple ? [2, 3, 4] : [2]);
   1.524 +    if (multiple) {
   1.525 +      synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "select", "key shift page down again");
   1.526 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page down again" + testidmod,
   1.527 +                                       7, [2, 3, 4, 5, 6, 7]);
   1.528 +      synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "!select", "key shift page down at start");
   1.529 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page down at start" + testidmod,
   1.530 +                                       7, [2, 3, 4, 5, 6, 7]);
   1.531 +      synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "select", "key shift page up deselect");
   1.532 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page up deselect" + testidmod,
   1.533 +                                       4, [2, 3, 4]);
   1.534 +    }
   1.535 +
   1.536 +    // test when page down / page up is pressed when the view is scrolled such
   1.537 +    // that the selection is not visible
   1.538 +    if (multiple) {
   1.539 +      tree.treeBoxObject.scrollToRow(3);
   1.540 +      selection.currentIndex = 1;
   1.541 +      selection.select(1);
   1.542 +      synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, eventExpected,
   1.543 +                               "key shift page down with view scrolled down");
   1.544 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page down with view scrolled down" + testidmod,
   1.545 +                                       6, [1, 2, 3, 4, 5, 6], 3);
   1.546 +
   1.547 +      tree.treeBoxObject.scrollToRow(2);
   1.548 +      selection.currentIndex = 6;
   1.549 +      selection.select(6);
   1.550 +      synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, eventExpected,
   1.551 +                               "key shift page up with view scrolled up");
   1.552 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page up with view scrolled up" + testidmod,
   1.553 +                                       2, [2, 3, 4, 5, 6], 2);
   1.554 +
   1.555 +      tree.treeBoxObject.scrollToRow(2);
   1.556 +      selection.currentIndex = 0;
   1.557 +      selection.select(0);
   1.558 +      // don't expect the select event, as the selection won't have changed
   1.559 +      synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "!select",
   1.560 +                               "key shift page up at start with view scrolled down");
   1.561 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page up at start with view scrolled down" + testidmod,
   1.562 +                                       0, [0], 0);
   1.563 +
   1.564 +      tree.treeBoxObject.scrollToRow(0);
   1.565 +      selection.currentIndex = 7;
   1.566 +      selection.select(7);
   1.567 +      // don't expect the select event, as the selection won't have changed
   1.568 +      synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "!select",
   1.569 +                               "key shift page down at end with view scrolled up");
   1.570 +      testtag_tree_TreeSelection_State(tree, testid + "key shift page down at end with view scrolled up" + testidmod,
   1.571 +                                       7, [7], 4);
   1.572 +    }
   1.573 +
   1.574 +    tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
   1.575 +  }
   1.576 +
   1.577 +  tree.treeBoxObject.scrollToRow(4);
   1.578 +  selection.select(5);
   1.579 +  synthesizeKeyExpectEvent("VK_HOME", { shiftKey: true }, tree, eventExpected, "key shift home");
   1.580 +  testtag_tree_TreeSelection_State(tree, testid + "key shift home",
   1.581 +                                   multiple ? 0 : 5, multiple ? [0, 1, 2, 3, 4, 5] : [5], multiple ? 0 : 4);
   1.582 +
   1.583 +  tree.treeBoxObject.scrollToRow(0);
   1.584 +  selection.select(3);
   1.585 +  synthesizeKeyExpectEvent("VK_END", { shiftKey: true }, tree, eventExpected, "key shift end");
   1.586 +  testtag_tree_TreeSelection_State(tree, testid + "key shift end",
   1.587 +                                   multiple ? 7 : 3, multiple ? [3, 4, 5, 6, 7] : [3], multiple ? 4 : 0);
   1.588 +
   1.589 +  // pressing space selects a row, pressing accel + space unselects a row
   1.590 +  selection.select(2);
   1.591 +  selection.currentIndex = 4;
   1.592 +  synthesizeKeyExpectEvent(" ", {}, tree, "select", "key space on");
   1.593 +  // in single selection mode, space shouldn't do anything
   1.594 +  testtag_tree_TreeSelection_State(tree, testid + "key space on", 4, multiple ? [2, 4] : [2]);
   1.595 +
   1.596 +  if (multiple) {
   1.597 +    synthesizeKeyExpectEvent(" ", { accelKey: true }, tree, "select", "key space off");
   1.598 +    testtag_tree_TreeSelection_State(tree, testid + "key space off", 4, [2]);
   1.599 +  }
   1.600 +
   1.601 +  // check that clicking on a row selects it
   1.602 +  tree.treeBoxObject.scrollToRow(0);
   1.603 +  selection.select(2);
   1.604 +  selection.currentIndex = 2;
   1.605 +  if (0) { // XXXndeakin disable these tests for now
   1.606 +    mouseOnCell(tree, 1, tree.columns[1], "mouse on row");
   1.607 +    testtag_tree_TreeSelection_State(tree, testid + "mouse on row", 1, [1], 0,
   1.608 +                                     tree.selType == "cell" ? tree.columns[1] : null);
   1.609 +  }
   1.610 +
   1.611 +  // restore the scroll position to the start of the page
   1.612 +  sendKey("HOME");
   1.613 +
   1.614 +  window.removeEventListener("keydown", keydownListener, false);
   1.615 +  window.removeEventListener("keypress", keypressListener, false);
   1.616 +  is(keydownFired, multiple ? 63 : 40, "keydown event wasn't fired properly");
   1.617 +  is(keypressFired, multiple ? 2 : 1, "keypress event wasn't fired properly");
   1.618 +}
   1.619 +
   1.620 +function testtag_tree_UI_editing(tree, testid, rowInfo)
   1.621 +{
   1.622 +  testid += " editing UI ";
   1.623 +
   1.624 +  // check editing UI
   1.625 +  var ecolumn = tree.columns[0];
   1.626 +  var rowIndex = 2;
   1.627 +  var inputField = tree.inputField;
   1.628 +
   1.629 +  // temporary make the tree editable to test mouse double click
   1.630 +  var wasEditable = tree.editable;
   1.631 +  if (!wasEditable)
   1.632 +    tree.editable = true;
   1.633 +
   1.634 +  // if this is a container save its current open status
   1.635 +  var row = rowInfo.rows[rowIndex];
   1.636 +  var wasOpen = null;
   1.637 +  if (tree.view.isContainer(row))
   1.638 +    wasOpen = tree.view.isContainerOpen(row);
   1.639 +
   1.640 +  // Test whether a keystroke can enter text entry, and another can exit.
   1.641 +  if (tree.selType == "cell")
   1.642 +  {
   1.643 +    tree.stopEditing(false);
   1.644 +    ok(!tree.editingColumn, "Should not be editing tree cell now");
   1.645 +    tree.view.selection.currentColumn = ecolumn;
   1.646 +    tree.currentIndex = rowIndex;
   1.647 +
   1.648 +    const isMac = (navigator.platform.indexOf("Mac") >= 0);
   1.649 +    const StartEditingKey = isMac ? "RETURN" : "F2";
   1.650 +    sendKey(StartEditingKey);
   1.651 +    is(tree.editingColumn, ecolumn, "Should be editing tree cell now");
   1.652 +    sendKey("ESCAPE");
   1.653 +    ok(!tree.editingColumn, "Should not be editing tree cell now");
   1.654 +    is(tree.currentIndex, rowIndex, "Current index should not have changed");
   1.655 +    is(tree.view.selection.currentColumn, ecolumn, "Current column should not have changed");
   1.656 +  }
   1.657 +
   1.658 +  mouseDblClickOnCell(tree, rowIndex, ecolumn, testid + "edit on double click");
   1.659 +  is(tree.editingColumn, ecolumn, testid + "editing column");
   1.660 +  is(tree.editingRow, rowIndex, testid + "editing row");
   1.661 +
   1.662 +  // ensure that we don't expand an expandable container on edit
   1.663 +  if (wasOpen != null)
   1.664 +    is(tree.view.isContainerOpen(row), wasOpen, testid + "opened container node on edit");
   1.665 +
   1.666 +  // ensure to restore editable attribute
   1.667 +  if (!wasEditable)
   1.668 +    tree.editable = false;
   1.669 +
   1.670 +  var ci = tree.currentIndex;
   1.671 +
   1.672 +  // cursor navigation should not change the selection while editing
   1.673 +  var testKey = function(key) {
   1.674 +    synthesizeKeyExpectEvent(key, {}, tree, "!select", "key " + key + " with editing");
   1.675 +    is(tree.editingRow == rowIndex && tree.editingColumn == ecolumn && tree.currentIndex == ci,
   1.676 +                  true, testid + "key " + key + " while editing");
   1.677 +  }
   1.678 +
   1.679 +  testKey("VK_DOWN");
   1.680 +  testKey("VK_UP");
   1.681 +  testKey("VK_PAGE_DOWN");
   1.682 +  testKey("VK_PAGE_UP");
   1.683 +  testKey("VK_HOME");
   1.684 +  testKey("VK_END");
   1.685 +
   1.686 +  // XXXndeakin figure out how to send characters to the textbox
   1.687 +  // inputField.inputField.focus()
   1.688 +  // synthesizeKeyExpectEvent(inputField.inputField, "b", null, "");
   1.689 +  // tree.stopEditing(true);
   1.690 +  // is(tree.view.getCellText(0, ecolumn), "b", testid + "edit cell");
   1.691 +
   1.692 +  // Restore initial state.
   1.693 +  tree.stopEditing(false);
   1.694 +}
   1.695 +
   1.696 +function testtag_tree_TreeSelection_UI_cell(tree, testid, rowInfo)
   1.697 +{
   1.698 +  testid += " selection UI cell ";
   1.699 +
   1.700 +  var columns = tree.columns;
   1.701 +  var firstcolumn = columns[0];
   1.702 +  var secondcolumn = columns[1];
   1.703 +  var lastcolumn = columns[columns.length - 1];
   1.704 +  var secondlastcolumn = columns[columns.length - 2];
   1.705 +  var selection = tree.view.selection;
   1.706 +
   1.707 +  selection.clearSelection();
   1.708 +  selection.currentIndex = -1;
   1.709 +  selection.currentColumn = firstcolumn;
   1.710 +  is(selection.currentColumn, firstcolumn, testid + " first currentColumn");
   1.711 +
   1.712 +  // no selection yet so nothing should happen when the left and right cursor keys are pressed
   1.713 +  synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right no selection");
   1.714 +  testtag_tree_TreeSelection_State(tree, testid + "key right no selection", -1, [], null, firstcolumn);
   1.715 +
   1.716 +  selection.currentColumn = secondcolumn;
   1.717 +  synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left no selection");
   1.718 +  testtag_tree_TreeSelection_State(tree, testid + "key left no selection", -1, [], null, secondcolumn);
   1.719 +
   1.720 +  selection.select(2);
   1.721 +  selection.currentIndex = 2;
   1.722 +  if (0) { // XXXndeakin disable these tests for now
   1.723 +    mouseOnCell(tree, 1, secondlastcolumn, "mouse on cell");
   1.724 +    testtag_tree_TreeSelection_State(tree, testid + "mouse on cell", 1, [1], null, secondlastcolumn);
   1.725 +  }
   1.726 +
   1.727 +  tree.focus();
   1.728 + 
   1.729 +  // selection is set, so it should move when the left and right cursor keys are pressed
   1.730 +  tree.treeBoxObject.scrollToRow(0);
   1.731 +  selection.select(1);
   1.732 +  selection.currentIndex = 1;
   1.733 +  selection.currentColumn = secondcolumn;
   1.734 +  synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left in second column");
   1.735 +  testtag_tree_TreeSelection_State(tree, testid + "key left in second column", 1, [1], 0, firstcolumn);
   1.736 +
   1.737 +  synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left in first column");
   1.738 +  testtag_tree_TreeSelection_State(tree, testid + "key left in first column", 1, [1], 0, firstcolumn);
   1.739 +
   1.740 +  selection.currentColumn = secondlastcolumn;
   1.741 +  synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right in second last column");
   1.742 +  testtag_tree_TreeSelection_State(tree, testid + "key right in second last column", 1, [1], 0, lastcolumn);
   1.743 +
   1.744 +  synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right in last column");
   1.745 +  testtag_tree_TreeSelection_State(tree, testid + "key right in last column", 1, [1], 0, lastcolumn);
   1.746 +
   1.747 +  synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up in second row");
   1.748 +  testtag_tree_TreeSelection_State(tree, testid + "key up in second row", 0, [0], 0, lastcolumn);
   1.749 +
   1.750 +  synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up in first row");
   1.751 +  testtag_tree_TreeSelection_State(tree, testid + "key up in first row", 0, [0], 0, lastcolumn);
   1.752 +
   1.753 +  synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down in first row");
   1.754 +  testtag_tree_TreeSelection_State(tree, testid + "key down in first row", 1, [1], 0, lastcolumn);
   1.755 +
   1.756 +  var lastidx = tree.view.rowCount - 1;
   1.757 +  tree.treeBoxObject.scrollToRow(lastidx - 3);
   1.758 +  selection.select(lastidx);
   1.759 +  selection.currentIndex = lastidx;
   1.760 +  synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down in last row");
   1.761 +  testtag_tree_TreeSelection_State(tree, testid + "key down in last row", lastidx, [lastidx], lastidx - 3, lastcolumn);
   1.762 +
   1.763 +  synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home");
   1.764 +  testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0, lastcolumn);
   1.765 +
   1.766 +  synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end");
   1.767 +  testtag_tree_TreeSelection_State(tree, testid + "key end", lastidx, [lastidx], lastidx - 3, lastcolumn);
   1.768 +
   1.769 +  for (var t = 0; t < 2; t++) {
   1.770 +    var testidmod = (t == 0) ? "" : " rev";
   1.771 +
   1.772 +    // scroll to the end, subtract 3 because we want lastidx to appear
   1.773 +    // at the end of view
   1.774 +    tree.treeBoxObject.scrollToRow(lastidx - 3);
   1.775 +    selection.select(lastidx);
   1.776 +    selection.currentIndex = lastidx;
   1.777 +    var expectedrow = tree.pageUpOrDownMovesSelection ? lastidx - 3 : lastidx;
   1.778 +    synthesizeKeyExpectEvent("VK_PAGE_UP", {}, tree, "!select", "key page up");
   1.779 +    testtag_tree_TreeSelection_State(tree, testid + "key page up" + testidmod,
   1.780 +                                     expectedrow, [expectedrow],
   1.781 +                                     tree.pageUpOrDownMovesSelection ? lastidx - 3 : 0, lastcolumn);
   1.782 +
   1.783 +    tree.treeBoxObject.scrollToRow(1);
   1.784 +    selection.select(1);
   1.785 +    selection.currentIndex = 1;
   1.786 +    var expectedrow = tree.pageUpOrDownMovesSelection ? 4 : 1;
   1.787 +    synthesizeKeyExpectEvent("VK_PAGE_DOWN", {}, tree, "!select", "key page down");
   1.788 +    testtag_tree_TreeSelection_State(tree, testid + "key page down" + testidmod,
   1.789 +                                     expectedrow, [expectedrow],
   1.790 +                                     tree.pageUpOrDownMovesSelection ? 1 : lastidx - 3, lastcolumn);
   1.791 +
   1.792 +    tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
   1.793 +  }
   1.794 +
   1.795 +  // now check navigation when there is unselctable column
   1.796 +  secondcolumn.element.setAttribute("selectable", "false");
   1.797 +  secondcolumn.invalidate();
   1.798 +  is(secondcolumn.selectable, false, testid + "set selectable attribute");
   1.799 +
   1.800 +  if (columns.length >= 3) {
   1.801 +    selection.select(3);
   1.802 +    selection.currentIndex = 3;
   1.803 +    // check whether unselectable columns are skipped over
   1.804 +    selection.currentColumn = firstcolumn;
   1.805 +    synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable column");
   1.806 +    testtag_tree_TreeSelection_State(tree, testid + "key right unselectable column",
   1.807 +                                     3, [3], null, secondcolumn.getNext());
   1.808 +
   1.809 +    synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable column");
   1.810 +    testtag_tree_TreeSelection_State(tree, testid + "key left unselectable column",
   1.811 +                                     3, [3], null, firstcolumn);
   1.812 +  }
   1.813 +
   1.814 +  secondcolumn.element.removeAttribute("selectable");
   1.815 +  secondcolumn.invalidate();
   1.816 +  is(secondcolumn.selectable, true, testid + "clear selectable attribute");
   1.817 +
   1.818 +  // check to ensure that navigation isn't allowed if the first column is not selectable
   1.819 +  selection.currentColumn = secondcolumn;
   1.820 +  firstcolumn.element.setAttribute("selectable", "false");
   1.821 +  firstcolumn.invalidate();
   1.822 +  synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable first column");
   1.823 +  testtag_tree_TreeSelection_State(tree, testid + "key left unselectable first column",
   1.824 +                                   3, [3], null, secondcolumn);
   1.825 +  firstcolumn.element.removeAttribute("selectable");
   1.826 +  firstcolumn.invalidate();
   1.827 +
   1.828 +  // check to ensure that navigation isn't allowed if the last column is not selectable
   1.829 +  selection.currentColumn = secondlastcolumn;
   1.830 +  lastcolumn.element.setAttribute("selectable", "false");
   1.831 +  lastcolumn.invalidate();
   1.832 +  synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable last column");
   1.833 +  testtag_tree_TreeSelection_State(tree, testid + "key right unselectable last column",
   1.834 +                                   3, [3], null, secondlastcolumn);
   1.835 +  lastcolumn.element.removeAttribute("selectable");
   1.836 +  lastcolumn.invalidate();
   1.837 +
   1.838 +  // now check for cells with selectable false
   1.839 +  if (!rowInfo.rows[4].cells[1].selectable && columns.length >= 3) {
   1.840 +    // check whether unselectable cells are skipped over
   1.841 +    selection.select(4);
   1.842 +    selection.currentIndex = 4;
   1.843 +
   1.844 +    selection.currentColumn = firstcolumn;
   1.845 +    synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable cell");
   1.846 +    testtag_tree_TreeSelection_State(tree, testid + "key right unselectable cell",
   1.847 +                                     4, [4], null, secondcolumn.getNext());
   1.848 +
   1.849 +    synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable cell");
   1.850 +    testtag_tree_TreeSelection_State(tree, testid + "key left unselectable cell",
   1.851 +                                     4, [4], null, firstcolumn);
   1.852 +
   1.853 +    tree.treeBoxObject.scrollToRow(1);
   1.854 +    selection.select(3);
   1.855 +    selection.currentIndex = 3;
   1.856 +    selection.currentColumn = secondcolumn;
   1.857 +
   1.858 +    synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down unselectable cell");
   1.859 +    testtag_tree_TreeSelection_State(tree, testid + "key down unselectable cell",
   1.860 +                                     5, [5], 2, secondcolumn);
   1.861 +
   1.862 +    tree.treeBoxObject.scrollToRow(4);
   1.863 +    synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up unselectable cell");
   1.864 +    testtag_tree_TreeSelection_State(tree, testid + "key up unselectable cell",
   1.865 +                                     3, [3], 3, secondcolumn);
   1.866 +  }
   1.867 +
   1.868 +  // restore the scroll position to the start of the page
   1.869 +  sendKey("HOME");
   1.870 +}
   1.871 +
   1.872 +function testtag_tree_TreeView(tree, testid, rowInfo)
   1.873 +{
   1.874 +  testid += " view ";
   1.875 +
   1.876 +  var columns = tree.columns;
   1.877 +  var view = tree.view;
   1.878 +
   1.879 +  is(view instanceof Components.interfaces.nsITreeView, true, testid + "view is a TreeView");
   1.880 +  is(view.rowCount, rowInfo.rows.length, testid + "rowCount");
   1.881 +
   1.882 +  testtag_tree_TreeView_rows(tree, testid, rowInfo, 0);
   1.883 +
   1.884 +  // note that this will only work for content trees currently
   1.885 +  view.setCellText(0, columns[1], "Changed Value");
   1.886 +  is(view.getCellText(0, columns[1]), "Changed Value", "setCellText");
   1.887 +
   1.888 +  view.setCellValue(1, columns[0], "Another Changed Value");
   1.889 +  is(view.getCellValue(1, columns[0]), "Another Changed Value", "setCellText");
   1.890 +}
   1.891 +
   1.892 +function testtag_tree_TreeView_rows(tree, testid, rowInfo, startRow)
   1.893 +{
   1.894 +  var r;
   1.895 +  var columns = tree.columns;
   1.896 +  var view = tree.view;
   1.897 +  var length = rowInfo.rows.length;
   1.898 +
   1.899 +  // methods to test along with the functions which determine the expected value
   1.900 +  var checkRowMethods =
   1.901 +  {
   1.902 +    isContainer: function(row) { return row.container },
   1.903 +    isContainerOpen: function(row) { return false },
   1.904 +    isContainerEmpty: function(row) { return (row.children != null && row.children.rows.length == 0) },
   1.905 +    isSeparator: function(row) { return row.separator },
   1.906 +    getRowProperties: function(row) { return row.properties },
   1.907 +    getLevel: function(row) { return row.level },
   1.908 +    getParentIndex: function(row) { return row.parent },
   1.909 +    hasNextSibling: function(row) { return r < startRow + length - 1; }
   1.910 +  };
   1.911 +
   1.912 +  var checkCellMethods =
   1.913 +  {
   1.914 +    getCellText: function(row, cell) { return cell.label },
   1.915 +    getCellValue: function(row, cell) { return cell.value },
   1.916 +    getCellProperties: function(row, cell) { return cell.properties },
   1.917 +    isEditable: function(row, cell) { return cell.editable },
   1.918 +    isSelectable: function(row, cell) { return cell.selectable },
   1.919 +    getImageSrc: function(row, cell) { return cell.image },
   1.920 +    getProgressMode: function(row, cell) { return cell.mode }
   1.921 +  };
   1.922 +
   1.923 +  var failedMethods = { };
   1.924 +  var checkMethod, actual, expected;
   1.925 +  var containerInfo = null;
   1.926 +  var toggleOpenStateOK = true;
   1.927 +
   1.928 +  for (r = startRow; r < length; r++) {
   1.929 +    var row = rowInfo.rows[r];
   1.930 +    for (var c = 0; c < row.cells.length; c++) {
   1.931 +      var cell = row.cells[c];
   1.932 +
   1.933 +      for (checkMethod in checkCellMethods) {
   1.934 +        expected = checkCellMethods[checkMethod](row, cell);
   1.935 +        actual = view[checkMethod](r, columns[c]);
   1.936 +        if (actual !== expected) {
   1.937 +          failedMethods[checkMethod] = true;
   1.938 +          is(actual, expected, testid + "row " + r + " column " + c + " " + checkMethod + " is incorrect");
   1.939 +        }
   1.940 +      }
   1.941 +    }
   1.942 +
   1.943 +    // compare row properties
   1.944 +    for (checkMethod in checkRowMethods) {
   1.945 +      expected = checkRowMethods[checkMethod](row, r);
   1.946 +      if (checkMethod == "hasNextSibling") {
   1.947 +        actual = view[checkMethod](r, r);
   1.948 +      }
   1.949 +      else {
   1.950 +        actual = view[checkMethod](r);
   1.951 +      }
   1.952 +      if (actual !== expected) {
   1.953 +        failedMethods[checkMethod] = true;
   1.954 +        is(actual, expected, testid + "row " + r + " " + checkMethod + " is incorrect");
   1.955 +      }
   1.956 +    }
   1.957 +/*
   1.958 +    // open and recurse into containers
   1.959 +    if (row.container) {
   1.960 +      view.toggleOpenState(r);
   1.961 +      if (!view.isContainerOpen(r)) {
   1.962 +        toggleOpenStateOK = false;
   1.963 +        is(view.isContainerOpen(r), true, testid + "row " + r + " toggleOpenState open");
   1.964 +      }
   1.965 +      testtag_tree_TreeView_rows(tree, testid + "container " + r + " ", row.children, r + 1);
   1.966 +      view.toggleOpenState(r);
   1.967 +      if (view.isContainerOpen(r)) {
   1.968 +        toggleOpenStateOK = false;
   1.969 +        is(view.isContainerOpen(r), false, testid + "row " + r + " toggleOpenState close");
   1.970 +      }
   1.971 +    }
   1.972 +*/
   1.973 +  }
   1.974 +
   1.975 +  for (var failedMethod in failedMethods) {
   1.976 +    if (failedMethod in checkRowMethods)
   1.977 +      delete checkRowMethods[failedMethod];
   1.978 +    if (failedMethod in checkCellMethods)
   1.979 +      delete checkCellMethods[failedMethod];
   1.980 +  }
   1.981 +
   1.982 +  for (checkMethod in checkRowMethods)
   1.983 +    is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod);
   1.984 +  for (checkMethod in checkCellMethods)
   1.985 +    is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod);
   1.986 +  if (toggleOpenStateOK)
   1.987 +    is("toggleOpenState ok", "toggleOpenState ok", testid + "toggleOpenState");
   1.988 +}
   1.989 +
   1.990 +function testtag_tree_TreeView_rows_sort(tree, testid, rowInfo)
   1.991 +{
   1.992 +  // check if cycleHeader sorts the columns
   1.993 +  var columnIndex = 0;
   1.994 +  var view = tree.view;
   1.995 +  var column = tree.columns[columnIndex];
   1.996 +  var columnElement = column.element;
   1.997 +  var sortkey = columnElement.getAttribute("sort");
   1.998 +  if (sortkey) {
   1.999 +    view.cycleHeader(column);
  1.1000 +    is(tree.getAttribute("sort"), sortkey, "cycleHeader sort");
  1.1001 +    is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending");
  1.1002 +    is(columnElement.getAttribute("sortDirection"), "ascending", "cycleHeader column sortDirection");
  1.1003 +    is(columnElement.getAttribute("sortActive"), "true", "cycleHeader column sortActive");
  1.1004 +    view.cycleHeader(column);
  1.1005 +    is(tree.getAttribute("sortDirection"), "descending", "cycleHeader sortDirection descending");
  1.1006 +    is(columnElement.getAttribute("sortDirection"), "descending", "cycleHeader column sortDirection descending");
  1.1007 +    view.cycleHeader(column);
  1.1008 +    is(tree.getAttribute("sortDirection"), "", "cycleHeader sortDirection natural");
  1.1009 +    is(columnElement.getAttribute("sortDirection"), "", "cycleHeader column sortDirection natural");
  1.1010 +    // XXXndeakin content view isSorted needs to be tested
  1.1011 +  }
  1.1012 +
  1.1013 +  // Check that clicking on column header sorts the column.
  1.1014 +  var columns = getSortedColumnArray(tree);
  1.1015 +  is(columnElement.getAttribute("sortDirection"), "",
  1.1016 +     "cycleHeader column sortDirection");
  1.1017 +
  1.1018 +  // Click once on column header and check sorting has cycled once.
  1.1019 +  mouseClickOnColumnHeader(columns, columnIndex, 0, 1);
  1.1020 +  is(columnElement.getAttribute("sortDirection"), "ascending",
  1.1021 +     "single click cycleHeader column sortDirection ascending");
  1.1022 +
  1.1023 +  // Now simulate a double click.
  1.1024 +  mouseClickOnColumnHeader(columns, columnIndex, 0, 2);
  1.1025 +  if (navigator.platform.indexOf("Win") == 0) {
  1.1026 +    // Windows cycles only once on double click.
  1.1027 +    is(columnElement.getAttribute("sortDirection"), "descending",
  1.1028 +       "double click cycleHeader column sortDirection descending");
  1.1029 +    // 1 single clicks should restore natural sorting.
  1.1030 +    mouseClickOnColumnHeader(columns, columnIndex, 0, 1);
  1.1031 +  }
  1.1032 +
  1.1033 +  // Check we have gone back to natural sorting.
  1.1034 +  is(columnElement.getAttribute("sortDirection"), "",
  1.1035 +     "cycleHeader column sortDirection");
  1.1036 +
  1.1037 +  columnElement.setAttribute("sorthints", "twostate");
  1.1038 +  view.cycleHeader(column);
  1.1039 +  is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending twostate");
  1.1040 +  view.cycleHeader(column);
  1.1041 +  is(tree.getAttribute("sortDirection"), "descending", "cycleHeader sortDirection ascending twostate");
  1.1042 +  view.cycleHeader(column);
  1.1043 +  is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending twostate again");
  1.1044 +  columnElement.removeAttribute("sorthints");
  1.1045 +  view.cycleHeader(column);
  1.1046 +  view.cycleHeader(column);
  1.1047 +
  1.1048 +  is(columnElement.getAttribute("sortDirection"), "",
  1.1049 +     "cycleHeader column sortDirection reset");
  1.1050 +}
  1.1051 +
  1.1052 +// checks if the current and selected rows are correct
  1.1053 +// current is the index of the current row
  1.1054 +// selected is an array of the indicies of the selected rows
  1.1055 +// column is the selected column
  1.1056 +// viewidx is the row that should be visible at the top of the tree
  1.1057 +function testtag_tree_TreeSelection_State(tree, testid, current, selected, viewidx, column)
  1.1058 +{
  1.1059 +  var selection = tree.view.selection;
  1.1060 +
  1.1061 +  if (!column)
  1.1062 +    column = (tree.selType == "cell") ? tree.columns[0] : null;
  1.1063 +
  1.1064 +  is(selection.count, selected.length, testid + " count");
  1.1065 +  is(tree.currentIndex, current, testid + " currentIndex");
  1.1066 +  is(selection.currentIndex, current, testid + " TreeSelection currentIndex");
  1.1067 +  is(selection.currentColumn, column, testid + " currentColumn");
  1.1068 +  if (viewidx !== null && viewidx !== undefined)
  1.1069 +    is(tree.treeBoxObject.getFirstVisibleRow(), viewidx, testid + " first visible row");
  1.1070 +
  1.1071 +  var actualSelected = [];
  1.1072 +  var count = tree.view.rowCount;
  1.1073 +  for (var s = 0; s < count; s++) {
  1.1074 +    if (selection.isSelected(s))
  1.1075 +      actualSelected.push(s);
  1.1076 +  }
  1.1077 +
  1.1078 +  is(compareArrays(selected, actualSelected), true, testid + " selection [" + selected + "]");
  1.1079 +
  1.1080 +  actualSelected = [];
  1.1081 +  var rangecount = selection.getRangeCount();
  1.1082 +  for (var r = 0; r < rangecount; r++) {
  1.1083 +    var start = {}, end = {};
  1.1084 +    selection.getRangeAt(r, start, end);
  1.1085 +    for (var rs = start.value; rs <= end.value; rs++)
  1.1086 +      actualSelected.push(rs);
  1.1087 +  }
  1.1088 +
  1.1089 +  is(compareArrays(selected, actualSelected), true, testid + " range selection [" + selected + "]");
  1.1090 +}
  1.1091 +
  1.1092 +function testtag_tree_column_reorder()
  1.1093 +{
  1.1094 +  // Make sure the tree is scrolled into the view, otherwise the test will
  1.1095 +  // fail
  1.1096 +  var testframe = window.parent.document.getElementById("testframe");
  1.1097 +  if (testframe) {
  1.1098 +    testframe.scrollIntoView();
  1.1099 +  }
  1.1100 +
  1.1101 +  var tree = document.getElementById("tree-column-reorder");
  1.1102 +  var numColumns = tree.columns.count;
  1.1103 +
  1.1104 +  var reference = [];
  1.1105 +  for (var i = 0; i < numColumns; i++) {
  1.1106 +    reference.push("col_" + i);
  1.1107 +  }
  1.1108 +
  1.1109 +  // Drag the first column to each position
  1.1110 +  for (var i = 0; i < numColumns - 1; i++) {
  1.1111 +    synthesizeColumnDrag(tree, i, i + 1, true);
  1.1112 +    arrayMove(reference, i, i + 1, true);
  1.1113 +    checkColumns(tree, reference, "drag first column right");
  1.1114 +  }
  1.1115 +
  1.1116 +  // And back
  1.1117 +  for (var i = numColumns - 1; i >= 1; i--) {
  1.1118 +    synthesizeColumnDrag(tree, i, i - 1, false);
  1.1119 +    arrayMove(reference, i, i - 1, false);
  1.1120 +    checkColumns(tree, reference, "drag last column left");
  1.1121 +  }
  1.1122 +
  1.1123 +  // Drag each column one column left
  1.1124 +  for (var i = 1; i < numColumns; i++) {
  1.1125 +    synthesizeColumnDrag(tree, i, i - 1, false);
  1.1126 +    arrayMove(reference, i, i - 1, false);
  1.1127 +    checkColumns(tree, reference, "drag each column left");
  1.1128 +  }
  1.1129 +
  1.1130 +  // And back
  1.1131 +  for (var i = numColumns - 2; i >= 0; i--) {
  1.1132 +    synthesizeColumnDrag(tree, i, i + 1, true);
  1.1133 +    arrayMove(reference, i, i + 1, true);
  1.1134 +    checkColumns(tree, reference, "drag each column right");
  1.1135 +  }
  1.1136 +
  1.1137 +  // Drag each column 5 to the right
  1.1138 +  for (var i = 0; i < numColumns - 5; i++) {
  1.1139 +    synthesizeColumnDrag(tree, i, i + 5, true);
  1.1140 +    arrayMove(reference, i, i + 5, true);
  1.1141 +    checkColumns(tree, reference, "drag each column 5 to the right");
  1.1142 +  }
  1.1143 +
  1.1144 +  // And to the left
  1.1145 +  for (var i = numColumns - 6; i >= 5; i--) {
  1.1146 +    synthesizeColumnDrag(tree, i, i - 5, false);
  1.1147 +    arrayMove(reference, i, i - 5, false);
  1.1148 +    checkColumns(tree, reference, "drag each column 5 to the left");
  1.1149 +  }
  1.1150 +
  1.1151 +  // Test that moving a column after itself does not move anything
  1.1152 +  synthesizeColumnDrag(tree, 0, 0, true);
  1.1153 +  checkColumns(tree, reference, "drag to itself");
  1.1154 +  is(document.treecolDragging, null, "drag to itself completed");
  1.1155 +
  1.1156 +  // XXX roc should this be here???
  1.1157 +  SimpleTest.finish();
  1.1158 +}
  1.1159 +
  1.1160 +function testtag_tree_wheel(aTree)
  1.1161 +{
  1.1162 +  const deltaModes = [
  1.1163 +    WheelEvent.DOM_DELTA_PIXEL,  // 0
  1.1164 +    WheelEvent.DOM_DELTA_LINE,   // 1
  1.1165 +    WheelEvent.DOM_DELTA_PAGE    // 2
  1.1166 +  ];
  1.1167 +  function helper(aStart, aDelta, aIntDelta, aDeltaMode)
  1.1168 +  {
  1.1169 +    aTree.treeBoxObject.scrollToRow(aStart);
  1.1170 +    var expected = !aIntDelta ? aStart :
  1.1171 +          aDeltaMode != WheelEvent.DOM_DELTA_PAGE ? aStart + aIntDelta :
  1.1172 +          aIntDelta > 0 ? aStart + aTree.treeBoxObject.getPageLength() :
  1.1173 +                          aStart - aTree.treeBoxObject.getPageLength();
  1.1174 +    if (expected < 0) {
  1.1175 +      expected = 0;
  1.1176 +    }
  1.1177 +    if (expected > aTree.view.rowCount - aTree.treeBoxObject.getPageLength()) {
  1.1178 +      expected = aTree.view.rowCount - aTree.treeBoxObject.getPageLength();
  1.1179 +    }
  1.1180 +    synthesizeWheel(aTree.body, 1, 1,
  1.1181 +                    { deltaMode: aDeltaMode, deltaY: aDelta,
  1.1182 +                      lineOrPageDeltaY: aIntDelta });
  1.1183 +    is(aTree.treeBoxObject.getFirstVisibleRow(), expected,
  1.1184 +         "testtag_tree_wheel: vertical, starting " + aStart +
  1.1185 +           " delta " + aDelta + " lineOrPageDelta " + aIntDelta +
  1.1186 +           " aDeltaMode " + aDeltaMode);
  1.1187 +
  1.1188 +    aTree.treeBoxObject.scrollToRow(aStart);
  1.1189 +    // Check that horizontal scrolling has no effect
  1.1190 +    synthesizeWheel(aTree.body, 1, 1,
  1.1191 +                    { deltaMode: aDeltaMode, deltaX: aDelta,
  1.1192 +                      lineOrPageDeltaX: aIntDelta });
  1.1193 +    is(aTree.treeBoxObject.getFirstVisibleRow(), aStart,
  1.1194 +         "testtag_tree_wheel: horizontal, starting " + aStart +
  1.1195 +           " delta " + aDelta + " lineOrPageDelta " + aIntDelta +
  1.1196 +           " aDeltaMode " + aDeltaMode);
  1.1197 +  }
  1.1198 +
  1.1199 +  var defaultPrevented = 0;
  1.1200 +
  1.1201 +  function wheelListener(event) {
  1.1202 +    defaultPrevented++;
  1.1203 +  }
  1.1204 +  window.addEventListener("wheel", wheelListener, false);
  1.1205 +
  1.1206 +  deltaModes.forEach(function(aDeltaMode) {
  1.1207 +    var delta = (aDeltaMode == WheelEvent.DOM_DELTA_PIXEL) ? 5.0 : 0.3;
  1.1208 +    helper(2, -delta,  0, aDeltaMode);
  1.1209 +    helper(2, -delta, -1, aDeltaMode);
  1.1210 +    helper(2,  delta,  0, aDeltaMode);
  1.1211 +    helper(2,  delta,  1, aDeltaMode);
  1.1212 +    helper(2, -2 * delta,  0, aDeltaMode);
  1.1213 +    helper(2, -2 * delta, -1, aDeltaMode);
  1.1214 +    helper(2,  2 * delta,  0, aDeltaMode);
  1.1215 +    helper(2,  2 * delta,  1, aDeltaMode);
  1.1216 +  });
  1.1217 +
  1.1218 +  window.removeEventListener("wheel", wheelListener, false);
  1.1219 +  is(defaultPrevented, 48, "wheel event default prevented");
  1.1220 +}
  1.1221 +
  1.1222 +function synthesizeColumnDrag(aTree, aMouseDownColumnNumber, aMouseUpColumnNumber, aAfter)
  1.1223 +{
  1.1224 +  var columns = getSortedColumnArray(aTree);
  1.1225 +
  1.1226 +  var down = columns[aMouseDownColumnNumber].element;
  1.1227 +  var up   = columns[aMouseUpColumnNumber].element;
  1.1228 +
  1.1229 +  // Target the initial mousedown in the middle of the column header so we
  1.1230 +  // avoid the extra hit test space given to the splitter
  1.1231 +  var columnWidth = down.boxObject.width;
  1.1232 +  var splitterHitWidth = columnWidth / 2;
  1.1233 +  synthesizeMouse(down, splitterHitWidth, 3, { type: "mousedown"});
  1.1234 +
  1.1235 +  var offsetX = 0;
  1.1236 +  if (aAfter) {
  1.1237 +    offsetX = columnWidth;
  1.1238 +  }
  1.1239 +
  1.1240 +  if (aMouseUpColumnNumber > aMouseDownColumnNumber) {
  1.1241 +    for (var i = aMouseDownColumnNumber; i <= aMouseUpColumnNumber; i++) {
  1.1242 +      var move = columns[i].element;
  1.1243 +      synthesizeMouse(move, offsetX, 3, { type: "mousemove"});
  1.1244 +    }
  1.1245 +  }
  1.1246 +  else {
  1.1247 +    for (var i = aMouseDownColumnNumber; i >= aMouseUpColumnNumber; i--) {
  1.1248 +      var move = columns[i].element;
  1.1249 +      synthesizeMouse(move, offsetX, 3, { type: "mousemove"});
  1.1250 +    }
  1.1251 +  }
  1.1252 +
  1.1253 +  synthesizeMouse(up, offsetX, 3, { type: "mouseup"});
  1.1254 +}
  1.1255 +
  1.1256 +function arrayMove(aArray, aFrom, aTo, aAfter)
  1.1257 +{
  1.1258 +  var o = aArray.splice(aFrom, 1)[0];
  1.1259 +  if (aTo > aFrom) {
  1.1260 +    aTo--;
  1.1261 +  }
  1.1262 +
  1.1263 +  if (aAfter) {
  1.1264 +    aTo++;
  1.1265 +  }
  1.1266 +
  1.1267 +  aArray.splice(aTo, 0, o);
  1.1268 +}
  1.1269 +
  1.1270 +function getSortedColumnArray(aTree)
  1.1271 +{
  1.1272 +  var columns = aTree.columns;
  1.1273 +  var a = [];
  1.1274 +  for (var i = 0; i < columns.length; i++) {
  1.1275 +    a.push(columns.getColumnAt(i));
  1.1276 +  }
  1.1277 +
  1.1278 +  a.sort(function(a, b) {
  1.1279 +    var o1 = parseInt(a.element.getAttribute("ordinal"));
  1.1280 +    var o2 = parseInt(b.element.getAttribute("ordinal"));
  1.1281 +    return o1 - o2;
  1.1282 +  });
  1.1283 +  return a;
  1.1284 +}
  1.1285 +
  1.1286 +function checkColumns(aTree, aReference, aMessage)
  1.1287 +{
  1.1288 +  var columns = getSortedColumnArray(aTree);
  1.1289 +  var ids = [];
  1.1290 +  columns.forEach(function(e) {
  1.1291 +    ids.push(e.element.id);
  1.1292 +  });
  1.1293 +  is(compareArrays(ids, aReference), true, aMessage);
  1.1294 +}
  1.1295 +
  1.1296 +function mouseOnCell(tree, row, column, testname)
  1.1297 +{
  1.1298 +  var x = {}, y = {}, width = {}, height = {};
  1.1299 +  tree.boxObject.getCoordsForCellItem(row, column, "text", x, y, width, height);
  1.1300 +
  1.1301 +  synthesizeMouseExpectEvent(tree.body, x.value, y.value, {}, tree, "select", testname);
  1.1302 +}
  1.1303 +
  1.1304 +function mouseClickOnColumnHeader(aColumns, aColumnIndex, aButton, aClickCount)
  1.1305 +{
  1.1306 +  var columnHeader = aColumns[aColumnIndex].element;
  1.1307 +  var columnHeaderRect = columnHeader.getBoundingClientRect();
  1.1308 +  var columnWidth = columnHeaderRect.right - columnHeaderRect.left;
  1.1309 +  // For multiple click we send separate click events, with increasing
  1.1310 +  // clickCount.  This simulates the common behavior of multiple clicks.
  1.1311 +  for (var i = 1; i <= aClickCount; i++) {
  1.1312 +    // Target the middle of the column header.
  1.1313 +    synthesizeMouse(columnHeader, columnWidth / 2, 3,
  1.1314 +                    { button: aButton,
  1.1315 +                      clickCount: i }, null);
  1.1316 +  }
  1.1317 +}
  1.1318 +
  1.1319 +function mouseDblClickOnCell(tree, row, column, testname)
  1.1320 +{
  1.1321 +  // select the row we will edit
  1.1322 +  var selection = tree.view.selection;
  1.1323 +  selection.select(row);
  1.1324 +  tree.treeBoxObject.ensureRowIsVisible(row);
  1.1325 +
  1.1326 +  // get cell coordinates
  1.1327 +  var x = {}, y = {}, width = {}, height = {};
  1.1328 +  tree.treeBoxObject.getCoordsForCellItem(row, column, "text", x, y, width, height);
  1.1329 +
  1.1330 +  synthesizeMouse(tree.body, x.value, y.value, { clickCount: 2 }, null);
  1.1331 +}
  1.1332 +
  1.1333 +function compareArrays(arr1, arr2)
  1.1334 +{
  1.1335 +  if (arr1.length != arr2.length)
  1.1336 +    return false;
  1.1337 +
  1.1338 +  for (var i = 0; i < arr1.length; i++) {
  1.1339 +    if (arr1[i] != arr2[i])
  1.1340 +      return false;
  1.1341 +  }
  1.1342 +
  1.1343 +  return true;
  1.1344 +}
  1.1345 +
  1.1346 +function convertProperties(arr)
  1.1347 +{
  1.1348 +  var results = [];
  1.1349 +  var count = arr.Count();
  1.1350 +  for (var i = 0; i < count; i++)
  1.1351 +    results.push(arr.GetElementAt(i).QueryInterface(Components.interfaces.nsIAtom).toString());
  1.1352 +
  1.1353 +  results.sort();
  1.1354 +  return results.join(" ");
  1.1355 +}
  1.1356 +
  1.1357 +function convertDOMtoTreeRowInfo(treechildren, level, rowidx)
  1.1358 +{
  1.1359 +  var obj = { rows: [] };
  1.1360 +
  1.1361 +  var parentidx = rowidx.value;
  1.1362 +
  1.1363 +  treechildren = treechildren.childNodes;
  1.1364 +  for (var r = 0; r < treechildren.length; r++) {
  1.1365 +    rowidx.value++;
  1.1366 +
  1.1367 +    var treeitem = treechildren[r];
  1.1368 +    if (treeitem.hasChildNodes()) {
  1.1369 +      var treerow = treeitem.firstChild;
  1.1370 +      var cellInfo = [];
  1.1371 +      for (var c = 0; c < treerow.childNodes.length; c++) {
  1.1372 +        var cell = treerow.childNodes[c];
  1.1373 +        cellInfo.push({ label: "" + cell.getAttribute("label"),
  1.1374 +                        value: cell.getAttribute("value"),
  1.1375 +                        properties: cell.getAttribute("properties"),
  1.1376 +                        editable: cell.getAttribute("editable") != "false",
  1.1377 +                        selectable: cell.getAttribute("selectable") != "false",
  1.1378 +                        image: cell.getAttribute("src"),
  1.1379 +                        mode: cell.hasAttribute("mode") ? parseInt(cell.getAttribute("mode")) : 3 });
  1.1380 +      }
  1.1381 +
  1.1382 +      var descendants = treeitem.lastChild;
  1.1383 +      var children = (treerow == descendants) ? null :
  1.1384 +                     convertDOMtoTreeRowInfo(descendants, level + 1, rowidx);
  1.1385 +      obj.rows.push({ cells: cellInfo,
  1.1386 +                      properties: treerow.getAttribute("properties"),
  1.1387 +                      container: treeitem.getAttribute("container") == "true",
  1.1388 +                      separator: treeitem.localName == "treeseparator",
  1.1389 +                      children: children,
  1.1390 +                      level: level,
  1.1391 +                      parent: parentidx });
  1.1392 +    }
  1.1393 +  }
  1.1394 +
  1.1395 +  return obj;
  1.1396 +}

mercurial