toolkit/content/tests/widgets/tree_shared.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 var columns_simpletree =
     2 [
     3   { name: "name", label: "Name", key: true, properties: "one two" },
     4   { name: "address", label: "Address" }
     5 ];
     7 var columns_hiertree =
     8 [
     9   { name: "name", label: "Name", primary: true, key: true, properties: "one two" },
    10   { name: "address", label: "Address" },
    11   { name: "planet", label: "Planet" },
    12   { name: "gender", label: "Gender", cycler: true }
    13 ];
    15 // XXXndeakin still to add some tests for:
    16 //   cycler columns, checkbox cells, progressmeter cells
    18 // this test function expects a tree to have 8 rows in it when it isn't
    19 // expanded. The tree should only display four rows at a time. If editable,
    20 // the cell at row 1 and column 0 must be editable, and the cell at row 2 and
    21 // column 1 must not be editable.
    22 function testtag_tree(treeid, treerowinfoid, seltype, columnstype, testid)
    23 {
    24   // Stop keystrokes that aren't handled by the tree from leaking out and
    25   // scrolling the main Mochitests window!
    26   function preventDefault(event) {
    27     event.preventDefault();
    28   }
    29   document.addEventListener("keypress", preventDefault, false);
    31   var multiple = (seltype == "multiple");
    33   var tree = document.getElementById(treeid);
    34   var treerowinfo = document.getElementById(treerowinfoid);
    35   var rowInfo;
    36   if (testid =="tree view")
    37     rowInfo = getCustomTreeViewCellInfo();
    38   else
    39     rowInfo = convertDOMtoTreeRowInfo(treerowinfo, 0, { value: -1 });
    40   var columnInfo = (columnstype == "simple") ? columns_simpletree : columns_hiertree;
    42   is(tree.view.selection.currentColumn, null, testid + " initial currentColumn");
    43   is(tree.selType, seltype == "multiple" ? "" : seltype, testid + " seltype");
    45   // note: the functions below should be in this order due to changes made in later tests
    47   // select the first column in cell selection mode so that the selection
    48   // functions can be tested
    49   if (seltype == "cell")
    50     tree.view.selection.currentColumn = tree.columns[0];
    52   testtag_tree_columns(tree, columnInfo, testid);
    53   testtag_tree_TreeSelection(tree, testid, multiple);
    54   testtag_tree_TreeSelection_UI(tree, testid, multiple);
    55   if (seltype == "cell")
    56     testtag_tree_TreeSelection_UI_cell(tree, testid, rowInfo);
    58   testtag_tree_TreeView(tree, testid, rowInfo);
    60   is(tree.editable, false, "tree should not be editable");
    61   // currently, the editable flag means that tree editing cannot be invoked
    62   // by the user. However, editing can still be started with a script.
    63   is(tree.editingRow, -1, testid + " initial editingRow");
    64   is(tree.editingColumn, null, testid + " initial editingColumn");
    66   testtag_tree_UI_editing(tree, testid, rowInfo);
    68   is(tree.editable, false, "tree should not be editable after testtag_tree_UI_editing");
    69   // currently, the editable flag means that tree editing cannot be invoked
    70   // by the user. However, editing can still be started with a script.
    71   is(tree.editingRow, -1, testid + " initial editingRow (continued)");
    72   is(tree.editingColumn, null, testid + " initial editingColumn (continued)");
    74   var ecolumn = tree.columns[0];
    75   ok(!tree.startEditing(1, ecolumn), "non-editable trees shouldn't start editing");
    76   is(tree.editingRow, -1, testid + " failed startEditing shouldn't set editingRow");
    77   is(tree.editingColumn, null, testid + " failed startEditing shouldn't set editingColumn");  
    79   tree.editable = true;
    81   ok(tree.startEditing(1, ecolumn), "startEditing should have returned true");
    82   is(tree.editingRow, 1, testid + " startEditing editingRow");
    83   is(tree.editingColumn, ecolumn, testid + " startEditing editingColumn");
    84   is(tree.getAttribute("editing"), "true", testid + " startEditing editing attribute");
    86   tree.stopEditing(true);
    87   is(tree.editingRow, -1, testid + " stopEditing editingRow");
    88   is(tree.editingColumn, null, testid + " stopEditing editingColumn");
    89   is(tree.hasAttribute("editing"), false, testid + " stopEditing editing attribute");
    91   tree.startEditing(-1, ecolumn);
    92   is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing -1 editingRow");
    93   tree.startEditing(15, ecolumn);
    94   is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing 15 editingRow");
    95   tree.startEditing(1, null);
    96   is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing null column editingRow");
    97   tree.startEditing(2, tree.columns[1]);
    98   is(tree.editingRow == -1 && tree.editingColumn == null, true, testid + " startEditing non editable cell editingRow");
   100   tree.startEditing(1, ecolumn);
   101   var inputField = tree.inputField;
   102   is(inputField instanceof Components.interfaces.nsIDOMXULTextBoxElement, true, testid + "inputField");
   103   inputField.value = "Changed Value";
   104   tree.stopEditing(true);
   105   is(tree.view.getCellText(1, ecolumn), "Changed Value", testid + "edit cell accept");
   107   // this cell can be edited, but stopEditing(false) means don't accept the change.
   108   tree.startEditing(1, ecolumn);
   109   inputField.value = "Second Value";
   110   tree.stopEditing(false);
   111   is(tree.view.getCellText(1, ecolumn), "Changed Value", testid + "edit cell no accept");
   113   tree.editable = false;
   115   // do the sorting tests last as it will cause the rows to rearrange
   116   // skip them for the custom tree view
   117   if (testid !="tree view")
   118     testtag_tree_TreeView_rows_sort(tree, testid, rowInfo);
   120   testtag_tree_wheel(tree);
   122   document.removeEventListener("keypress", preventDefault, false);
   124   SimpleTest.finish();
   125 }
   127 function testtag_tree_columns(tree, expectedColumns, testid)
   128 {
   129   testid += " ";
   131   var columns = tree.columns;
   133   is(columns instanceof TreeColumns, true, testid + "columns is a TreeColumns");
   134   is(columns.count, expectedColumns.length, testid + "TreeColumns count");
   135   is(columns.length, expectedColumns.length, testid + "TreeColumns length");
   137   var treecols = tree.getElementsByTagName("treecols")[0];
   138   var treecol = treecols.getElementsByTagName("treecol");
   140   var x = 0;
   141   var primary = null, sorted = null, key = null;
   142   for (var c = 0; c < expectedColumns.length; c++) {
   143     var adjtestid = testid + " column " + c + " ";
   144     var column = columns[c];
   145     var expectedColumn = expectedColumns[c];
   146     is(columns.getColumnAt(c), column, adjtestid + "getColumnAt");
   147     is(columns.getNamedColumn(expectedColumn.name), column, adjtestid + "getNamedColumn");
   148     is(columns.getColumnFor(treecol[c]), column, adjtestid + "getColumnFor");
   149     if (expectedColumn.primary)
   150       primary = column;
   151     if (expectedColumn.sorted)
   152       sorted = column;
   153     if (expectedColumn.key)
   154       key = column;
   156     // XXXndeakin on Windows and Linux, some columns are one pixel to the
   157     // left of where they should be. Could just be a rounding issue.
   158     var adj = 1;
   159     is(column.x + adj >= x, true, adjtestid + "position is after last column " +
   160        column.x + "," + column.width + "," + x);
   161     is(column.width > 0, true, adjtestid + "width is greater than 0");
   162     x = column.x + column.width;
   164     // now check the TreeColumn properties
   165     is(column instanceof TreeColumn, true, adjtestid + "is a TreeColumn");
   166     is(column.element, treecol[c], adjtestid + "element is treecol");
   167     is(column.columns, columns, adjtestid + "columns is TreeColumns");
   168     is(column.id, expectedColumn.name, adjtestid + "name");
   169     is(column.index, c, adjtestid + "index");
   170     is(column.primary, primary == column, adjtestid + "column is primary");
   172     is(column.cycler, "cycler" in expectedColumn && expectedColumn.cycler,
   173                   adjtestid + "column is cycler");
   174     is(column.selectable, true, adjtestid + "column is selectable");
   175     is(column.editable, "editable" in expectedColumn && expectedColumn.editable,
   176                   adjtestid + "column is editable");
   178     is(column.type, "type" in expectedColumn ? expectedColumn.type : 1, adjtestid + "type");
   180     is(column.getPrevious(), c > 0 ? columns[c - 1] : null, adjtestid + "getPrevious");
   181     is(column.getNext(), c < columns.length - 1 ? columns[c + 1] : null, adjtestid + "getNext");
   183     // check the view's getColumnProperties method
   184     var properties = tree.view.getColumnProperties(column);
   185     var expectedProperties = expectedColumn.properties;
   186     is(properties,  expectedProperties ? expectedProperties : "", adjtestid + "getColumnProperties");
   187   }
   189   is(columns.getFirstColumn(), columns[0], testid + "getFirstColumn");
   190   is(columns.getLastColumn(), columns[columns.length - 1], testid + "getLastColumn");
   191   is(columns.getPrimaryColumn(), primary, testid + "getPrimaryColumn");
   192   is(columns.getSortedColumn(), sorted, testid + "getSortedColumn");
   193   is(columns.getKeyColumn(), key, testid + "getKeyColumn");
   195   is(columns.getColumnAt(-1), null, testid + "getColumnAt under");
   196   is(columns.getColumnAt(columns.length), null, testid + "getColumnAt over");
   197   is(columns.getNamedColumn(""), null, testid + "getNamedColumn null");
   198   is(columns.getNamedColumn("unknown"), null, testid + "getNamedColumn unknown");
   199   is(columns.getColumnFor(null), null, testid + "getColumnFor null");
   200   is(columns.getColumnFor(tree), null, testid + "getColumnFor other");
   201 }
   203 function testtag_tree_TreeSelection(tree, testid, multiple)
   204 {
   205   testid += " selection ";
   207   var selection = tree.view.selection;
   208   is(selection instanceof Components.interfaces.nsITreeSelection, true,
   209                 testid + "selection is a TreeSelection");
   210   is(selection.single, !multiple, testid + "single");
   212   testtag_tree_TreeSelection_State(tree, testid + "initial", -1, []);
   213   is(selection.shiftSelectPivot, -1, testid + "initial shiftSelectPivot");
   215   selection.currentIndex = 2;
   216   testtag_tree_TreeSelection_State(tree, testid + "set currentIndex", 2, []);
   217   tree.currentIndex = 3;
   218   testtag_tree_TreeSelection_State(tree, testid + "set tree.currentIndex", 3, []);
   220   // test the select() method, which should deselect all rows and select
   221   // a single row
   222   selection.select(1);
   223   testtag_tree_TreeSelection_State(tree, testid + "select 1", 1, [1]);
   224   selection.select(3);
   225   testtag_tree_TreeSelection_State(tree, testid + "select 2", 3, [3]);
   226   selection.select(3);
   227   testtag_tree_TreeSelection_State(tree, testid + "select same", 3, [3]);
   229   selection.currentIndex = 1;
   230   testtag_tree_TreeSelection_State(tree, testid + "set currentIndex with single selection", 1, [3]);
   232   tree.currentIndex = 2;
   233   testtag_tree_TreeSelection_State(tree, testid + "set tree.currentIndex with single selection", 2, [3]);
   235   // check the toggleSelect method. In single selection mode, it only toggles on when
   236   // there isn't currently a selection.
   237   selection.toggleSelect(2);
   238   testtag_tree_TreeSelection_State(tree, testid + "toggleSelect 1", 2, multiple ? [2, 3] : [3]);
   239   selection.toggleSelect(2);
   240   selection.toggleSelect(3);
   241   testtag_tree_TreeSelection_State(tree, testid + "toggleSelect 2", 3, []);
   243   // the current index doesn't change after a selectAll, so it should still be set to 1
   244   // selectAll has no effect on single selection trees
   245   selection.currentIndex = 1;
   246   selection.selectAll();
   247   testtag_tree_TreeSelection_State(tree, testid + "selectAll 1", 1, multiple ? [0, 1, 2, 3, 4, 5, 6 , 7] : []);
   248   selection.toggleSelect(2);
   249   testtag_tree_TreeSelection_State(tree, testid + "toggleSelect after selectAll", 2,
   250                                    multiple ? [0, 1, 3, 4, 5, 6, 7] : [2]);
   251   selection.clearSelection();
   252   testtag_tree_TreeSelection_State(tree, testid + "clearSelection", 2, []);
   253   selection.toggleSelect(3);
   254   selection.toggleSelect(1);
   255   if (multiple) {
   256     selection.selectAll();
   257     testtag_tree_TreeSelection_State(tree, testid + "selectAll 2", 1, [0, 1, 2, 3, 4, 5, 6, 7]);
   258   }
   259   selection.currentIndex = 2;
   260   selection.clearSelection();
   261   testtag_tree_TreeSelection_State(tree, testid + "clearSelection after selectAll", 2, []);
   263   // XXXndeakin invertSelection isn't implemented
   264   //  selection.invertSelection();
   266   is(selection.shiftSelectPivot, -1, testid + "shiftSelectPivot set to -1");
   268   // rangedSelect and clearRange set the currentIndex to the endIndex. The
   269   // shiftSelectPivot property will be set to startIndex.
   270   selection.rangedSelect(1, 3, false);
   271   testtag_tree_TreeSelection_State(tree, testid + "rangedSelect no augment",
   272                                    multiple ? 3 : 2, multiple ? [1, 2, 3] : []);
   273   is(selection.shiftSelectPivot, multiple ? 1 : -1,
   274                 testid + "shiftSelectPivot after rangedSelect no augment");
   275   if (multiple) {
   276     selection.select(1);
   277     selection.rangedSelect(0, 2, true);
   278     testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment", 2, [0, 1, 2]);
   279     is(selection.shiftSelectPivot, 0, testid + "shiftSelectPivot after rangedSelect augment");
   281     selection.clearRange(1, 3);
   282     testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment", 3, [0]);
   284     // check that rangedSelect can take a start value higher than end
   285     selection.rangedSelect(3, 1, false);
   286     testtag_tree_TreeSelection_State(tree, testid + "rangedSelect reverse", 1, [1, 2, 3]);
   287     is(selection.shiftSelectPivot, 3, testid + "shiftSelectPivot after rangedSelect reverse");
   289     // check that setting the current index doesn't change the selection
   290     selection.currentIndex = 0;
   291     testtag_tree_TreeSelection_State(tree, testid + "currentIndex with range selection", 0, [1, 2, 3]);
   292   }
   294   // both values of rangedSelect may be the same
   295   selection.rangedSelect(2, 2, false);
   296   testtag_tree_TreeSelection_State(tree, testid + "rangedSelect one row", 2, [2]);
   297   is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot after selecting one row");
   299   if (multiple) {
   300     selection.rangedSelect(2, 3, true);
   302     // a start index of -1 means from the last point
   303     selection.rangedSelect(-1, 0, true);
   304     testtag_tree_TreeSelection_State(tree, testid + "rangedSelect -1 existing selection", 0, [0, 1, 2, 3]);
   305     is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot after -1 existing selection");
   307     selection.currentIndex = 2;
   308     selection.rangedSelect(-1, 0, false);
   309     testtag_tree_TreeSelection_State(tree, testid + "rangedSelect -1 from currentIndex", 0, [0, 1, 2]);
   310     is(selection.shiftSelectPivot, 2, testid + "shiftSelectPivot -1 from currentIndex");
   311   }
   313   // XXXndeakin need to test out of range values but these don't work properly
   314 /*
   315   selection.select(-1);
   316   testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment -1", -1, []);
   318   selection.select(8);
   319   testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment 8", 3, [0]);
   320 */
   321 }
   323 function testtag_tree_TreeSelection_UI(tree, testid, multiple)
   324 {
   325   testid += " selection UI ";
   327   var selection = tree.view.selection;
   328   selection.clearSelection();
   329   selection.currentIndex = 0;
   330   tree.focus();
   332   var keydownFired = 0;
   333   var keypressFired = 0;
   334   function keydownListener(event)
   335   {
   336     keydownFired++;
   337   }
   338   function keypressListener(event) {
   339     keypressFired++;
   340   }
   342   // check that cursor up and down keys navigate up and down
   343   // select event fires after a delay so don't expect it. The reason it fires after a delay
   344   // is so that cursor navigation allows quicking skimming over a set of items without
   345   // actually firing events in-between, improving performance. The select event will only
   346   // be fired on the row where the cursor stops.
   347   window.addEventListener("keydown", keydownListener, false);
   348   window.addEventListener("keypress", keypressListener, false);
   350   synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down");
   351   testtag_tree_TreeSelection_State(tree, testid + "key down", 1, [1], 0);
   353   synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up");
   354   testtag_tree_TreeSelection_State(tree, testid + "key up", 0, [0], 0);
   356   synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up at start");
   357   testtag_tree_TreeSelection_State(tree, testid + "key up at start", 0, [0], 0);
   359   // pressing down while the last row is selected should not fire a select event,
   360   // as the selection won't have changed. Also the view is not scrolled in this case.
   361   selection.select(7);
   362   synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down at end");
   363   testtag_tree_TreeSelection_State(tree, testid + "key down at end", 7, [7], 0);
   365   // pressing keys while at the edge of the visible rows should scroll the list
   366   tree.treeBoxObject.scrollToRow(4);
   367   selection.select(4);
   368   synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up with scroll");
   369   is(tree.treeBoxObject.getFirstVisibleRow(), 3, testid + "key up with scroll");
   371   tree.treeBoxObject.scrollToRow(0);
   372   selection.select(3);
   373   synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down with scroll");
   374   is(tree.treeBoxObject.getFirstVisibleRow(), 1, testid + "key down with scroll");
   376   // accel key and cursor movement adjust currentIndex but should not change
   377   // the selection. In single selection mode, the selection will not change,
   378   // but instead will just scroll up or down a line
   379   tree.treeBoxObject.scrollToRow(0);
   380   selection.select(1);
   381   synthesizeKeyExpectEvent("VK_DOWN", { accelKey: true }, tree, "!select", "key down with accel");
   382   testtag_tree_TreeSelection_State(tree, testid + "key down with accel", multiple ? 2 : 1, [1]);
   383   if (!multiple)
   384     is(tree.treeBoxObject.getFirstVisibleRow(), 1, testid + "key down with accel and scroll");
   386   tree.treeBoxObject.scrollToRow(4);
   387   selection.select(4);
   388   synthesizeKeyExpectEvent("VK_UP", { accelKey: true }, tree, "!select", "key up with accel");
   389   testtag_tree_TreeSelection_State(tree, testid + "key up with accel", multiple ? 3 : 4, [4]);
   390   if (!multiple)
   391     is(tree.treeBoxObject.getFirstVisibleRow(), 3, testid + "key up with accel and scroll");
   393   // do this three times, one for each state of pageUpOrDownMovesSelection,
   394   // and then once with the accel key pressed
   395   for (var t = 0; t < 3; t++) {
   396     var testidmod = (t == 2) ? " with accel" : (t == 1) ? " rev" : "";
   397     var keymod = (t == 2) ? { accelKey: true } : { };
   399     var moveselection = tree.pageUpOrDownMovesSelection;
   400     if (t == 2)
   401       moveselection = !moveselection;
   403     tree.treeBoxObject.scrollToRow(4);
   404     selection.currentIndex = 6;
   405     selection.select(6);
   406     var expected = moveselection ? 4 : 6;
   407     synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up");
   408     testtag_tree_TreeSelection_State(tree, testid + "key page up" + testidmod,
   409                                      expected, [expected], moveselection ? 4 : 0);
   411     expected = moveselection ? 0 : 6;
   412     synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up again");
   413     testtag_tree_TreeSelection_State(tree, testid + "key page up again" + testidmod,
   414                                      expected, [expected], 0);
   416     expected = moveselection ? 0 : 6;
   417     synthesizeKeyExpectEvent("VK_PAGE_UP", keymod, tree, "!select", "key page up at start");
   418     testtag_tree_TreeSelection_State(tree, testid + "key page up at start" + testidmod,
   419                                      expected, [expected], 0);
   421     tree.treeBoxObject.scrollToRow(0);
   422     selection.currentIndex = 1;
   423     selection.select(1);
   424     expected = moveselection ? 3 : 1;
   425     synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down");
   426     testtag_tree_TreeSelection_State(tree, testid + "key page down" + testidmod,
   427                                      expected, [expected], moveselection ? 0 : 4);
   429     expected = moveselection ? 7 : 1;
   430     synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down again");
   431     testtag_tree_TreeSelection_State(tree, testid + "key page down again" + testidmod,
   432                                      expected, [expected], 4);
   434     expected = moveselection ? 7 : 1;
   435     synthesizeKeyExpectEvent("VK_PAGE_DOWN", keymod, tree, "!select", "key page down at start");
   436     testtag_tree_TreeSelection_State(tree, testid + "key page down at start" + testidmod,
   437                                      expected, [expected], 4);
   439     if (t < 2)
   440       tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
   441   }
   443   tree.treeBoxObject.scrollToRow(4);
   444   selection.select(6);
   445   synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home");
   446   testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0);
   448   tree.treeBoxObject.scrollToRow(0);
   449   selection.select(1);
   450   synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end");
   451   testtag_tree_TreeSelection_State(tree, testid + "key end", 7, [7], 4);
   453   // in single selection mode, the selection doesn't change in this case
   454   tree.treeBoxObject.scrollToRow(4);
   455   selection.select(6);
   456   synthesizeKeyExpectEvent("VK_HOME", { accelKey: true }, tree, "!select", "key home with accel");
   457   testtag_tree_TreeSelection_State(tree, testid + "key home with accel", multiple ? 0 : 6, [6], 0);
   459   tree.treeBoxObject.scrollToRow(0);
   460   selection.select(1);
   461   synthesizeKeyExpectEvent("VK_END", { accelKey: true }, tree, "!select", "key end with accel");
   462   testtag_tree_TreeSelection_State(tree, testid + "key end with accel", multiple ? 7 : 1, [1], 4);
   464   // next, test cursor navigation with selection. Here the select event will be fired
   465   selection.select(1);
   466   var eventExpected = multiple ? "select" : "!select";
   467   synthesizeKeyExpectEvent("VK_DOWN", { shiftKey: true }, tree, eventExpected, "key shift down to select");
   468   testtag_tree_TreeSelection_State(tree, testid + "key shift down to select",
   469                                    multiple ? 2 : 1, multiple ? [1, 2] : [1]);
   470   is(selection.shiftSelectPivot, multiple ? 1 : -1,
   471                 testid + "key shift down to select shiftSelectPivot");
   472   synthesizeKeyExpectEvent("VK_UP", { shiftKey: true }, tree, eventExpected, "key shift up to unselect");
   473   testtag_tree_TreeSelection_State(tree, testid + "key shift up to unselect", 1, [1]);
   474   is(selection.shiftSelectPivot, multiple ? 1 : -1,
   475                 testid + "key shift up to unselect shiftSelectPivot");
   476   if (multiple) {
   477     synthesizeKeyExpectEvent("VK_UP", { shiftKey: true }, tree, "select", "key shift up to select");
   478     testtag_tree_TreeSelection_State(tree, testid + "key shift up to select", 0, [0, 1]);
   479     is(selection.shiftSelectPivot, 1, testid + "key shift up to select shiftSelectPivot");
   480     synthesizeKeyExpectEvent("VK_DOWN", { shiftKey: true }, tree, "select", "key shift down to unselect");
   481     testtag_tree_TreeSelection_State(tree, testid + "key shift down to unselect", 1, [1]);
   482     is(selection.shiftSelectPivot, 1, testid + "key shift down to unselect shiftSelectPivot");
   483   }
   485   // do this twice, one for each state of pageUpOrDownMovesSelection, however
   486   // when selecting with the shift key, pageUpOrDownMovesSelection is ignored
   487   // and the selection always changes
   488   var lastidx = tree.view.rowCount - 1;
   489   for (var t = 0; t < 2; t++) {
   490     var testidmod = (t == 0) ? "" : " rev";
   492     // If the top or bottom visible row is the current row, pressing shift and
   493     // page down / page up selects one page up or one page down. Otherwise, the
   494     // selection is made to the top or bottom of the visible area.
   495     tree.treeBoxObject.scrollToRow(lastidx - 3);
   496     selection.currentIndex = 6;
   497     selection.select(6);
   498     synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, eventExpected, "key shift page up");
   499     testtag_tree_TreeSelection_State(tree, testid + "key shift page up" + testidmod,
   500                                      multiple ? 4 : 6, multiple ? [4, 5, 6] : [6]);
   501     if (multiple) {
   502       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "select", "key shift page up again");
   503       testtag_tree_TreeSelection_State(tree, testid + "key shift page up again" + testidmod,
   504                                        0, [0, 1, 2, 3, 4, 5, 6]);
   505       // no change in the selection, so no select event should be fired
   506       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "!select", "key shift page up at start");
   507       testtag_tree_TreeSelection_State(tree, testid + "key shift page up at start" + testidmod,
   508                                        0, [0, 1, 2, 3, 4, 5, 6]);
   509       // deselect by paging down again
   510       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "select", "key shift page down deselect");
   511       testtag_tree_TreeSelection_State(tree, testid + "key shift page down deselect" + testidmod,
   512                                        3, [3, 4, 5, 6]);
   513     }
   515     tree.treeBoxObject.scrollToRow(1);
   516     selection.currentIndex = 2;
   517     selection.select(2);
   518     synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, eventExpected, "key shift page down");
   519     testtag_tree_TreeSelection_State(tree, testid + "key shift page down" + testidmod,
   520                                      multiple ? 4 : 2, multiple ? [2, 3, 4] : [2]);
   521     if (multiple) {
   522       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "select", "key shift page down again");
   523       testtag_tree_TreeSelection_State(tree, testid + "key shift page down again" + testidmod,
   524                                        7, [2, 3, 4, 5, 6, 7]);
   525       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "!select", "key shift page down at start");
   526       testtag_tree_TreeSelection_State(tree, testid + "key shift page down at start" + testidmod,
   527                                        7, [2, 3, 4, 5, 6, 7]);
   528       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "select", "key shift page up deselect");
   529       testtag_tree_TreeSelection_State(tree, testid + "key shift page up deselect" + testidmod,
   530                                        4, [2, 3, 4]);
   531     }
   533     // test when page down / page up is pressed when the view is scrolled such
   534     // that the selection is not visible
   535     if (multiple) {
   536       tree.treeBoxObject.scrollToRow(3);
   537       selection.currentIndex = 1;
   538       selection.select(1);
   539       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, eventExpected,
   540                                "key shift page down with view scrolled down");
   541       testtag_tree_TreeSelection_State(tree, testid + "key shift page down with view scrolled down" + testidmod,
   542                                        6, [1, 2, 3, 4, 5, 6], 3);
   544       tree.treeBoxObject.scrollToRow(2);
   545       selection.currentIndex = 6;
   546       selection.select(6);
   547       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, eventExpected,
   548                                "key shift page up with view scrolled up");
   549       testtag_tree_TreeSelection_State(tree, testid + "key shift page up with view scrolled up" + testidmod,
   550                                        2, [2, 3, 4, 5, 6], 2);
   552       tree.treeBoxObject.scrollToRow(2);
   553       selection.currentIndex = 0;
   554       selection.select(0);
   555       // don't expect the select event, as the selection won't have changed
   556       synthesizeKeyExpectEvent("VK_PAGE_UP", { shiftKey: true }, tree, "!select",
   557                                "key shift page up at start with view scrolled down");
   558       testtag_tree_TreeSelection_State(tree, testid + "key shift page up at start with view scrolled down" + testidmod,
   559                                        0, [0], 0);
   561       tree.treeBoxObject.scrollToRow(0);
   562       selection.currentIndex = 7;
   563       selection.select(7);
   564       // don't expect the select event, as the selection won't have changed
   565       synthesizeKeyExpectEvent("VK_PAGE_DOWN", { shiftKey: true }, tree, "!select",
   566                                "key shift page down at end with view scrolled up");
   567       testtag_tree_TreeSelection_State(tree, testid + "key shift page down at end with view scrolled up" + testidmod,
   568                                        7, [7], 4);
   569     }
   571     tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
   572   }
   574   tree.treeBoxObject.scrollToRow(4);
   575   selection.select(5);
   576   synthesizeKeyExpectEvent("VK_HOME", { shiftKey: true }, tree, eventExpected, "key shift home");
   577   testtag_tree_TreeSelection_State(tree, testid + "key shift home",
   578                                    multiple ? 0 : 5, multiple ? [0, 1, 2, 3, 4, 5] : [5], multiple ? 0 : 4);
   580   tree.treeBoxObject.scrollToRow(0);
   581   selection.select(3);
   582   synthesizeKeyExpectEvent("VK_END", { shiftKey: true }, tree, eventExpected, "key shift end");
   583   testtag_tree_TreeSelection_State(tree, testid + "key shift end",
   584                                    multiple ? 7 : 3, multiple ? [3, 4, 5, 6, 7] : [3], multiple ? 4 : 0);
   586   // pressing space selects a row, pressing accel + space unselects a row
   587   selection.select(2);
   588   selection.currentIndex = 4;
   589   synthesizeKeyExpectEvent(" ", {}, tree, "select", "key space on");
   590   // in single selection mode, space shouldn't do anything
   591   testtag_tree_TreeSelection_State(tree, testid + "key space on", 4, multiple ? [2, 4] : [2]);
   593   if (multiple) {
   594     synthesizeKeyExpectEvent(" ", { accelKey: true }, tree, "select", "key space off");
   595     testtag_tree_TreeSelection_State(tree, testid + "key space off", 4, [2]);
   596   }
   598   // check that clicking on a row selects it
   599   tree.treeBoxObject.scrollToRow(0);
   600   selection.select(2);
   601   selection.currentIndex = 2;
   602   if (0) { // XXXndeakin disable these tests for now
   603     mouseOnCell(tree, 1, tree.columns[1], "mouse on row");
   604     testtag_tree_TreeSelection_State(tree, testid + "mouse on row", 1, [1], 0,
   605                                      tree.selType == "cell" ? tree.columns[1] : null);
   606   }
   608   // restore the scroll position to the start of the page
   609   sendKey("HOME");
   611   window.removeEventListener("keydown", keydownListener, false);
   612   window.removeEventListener("keypress", keypressListener, false);
   613   is(keydownFired, multiple ? 63 : 40, "keydown event wasn't fired properly");
   614   is(keypressFired, multiple ? 2 : 1, "keypress event wasn't fired properly");
   615 }
   617 function testtag_tree_UI_editing(tree, testid, rowInfo)
   618 {
   619   testid += " editing UI ";
   621   // check editing UI
   622   var ecolumn = tree.columns[0];
   623   var rowIndex = 2;
   624   var inputField = tree.inputField;
   626   // temporary make the tree editable to test mouse double click
   627   var wasEditable = tree.editable;
   628   if (!wasEditable)
   629     tree.editable = true;
   631   // if this is a container save its current open status
   632   var row = rowInfo.rows[rowIndex];
   633   var wasOpen = null;
   634   if (tree.view.isContainer(row))
   635     wasOpen = tree.view.isContainerOpen(row);
   637   // Test whether a keystroke can enter text entry, and another can exit.
   638   if (tree.selType == "cell")
   639   {
   640     tree.stopEditing(false);
   641     ok(!tree.editingColumn, "Should not be editing tree cell now");
   642     tree.view.selection.currentColumn = ecolumn;
   643     tree.currentIndex = rowIndex;
   645     const isMac = (navigator.platform.indexOf("Mac") >= 0);
   646     const StartEditingKey = isMac ? "RETURN" : "F2";
   647     sendKey(StartEditingKey);
   648     is(tree.editingColumn, ecolumn, "Should be editing tree cell now");
   649     sendKey("ESCAPE");
   650     ok(!tree.editingColumn, "Should not be editing tree cell now");
   651     is(tree.currentIndex, rowIndex, "Current index should not have changed");
   652     is(tree.view.selection.currentColumn, ecolumn, "Current column should not have changed");
   653   }
   655   mouseDblClickOnCell(tree, rowIndex, ecolumn, testid + "edit on double click");
   656   is(tree.editingColumn, ecolumn, testid + "editing column");
   657   is(tree.editingRow, rowIndex, testid + "editing row");
   659   // ensure that we don't expand an expandable container on edit
   660   if (wasOpen != null)
   661     is(tree.view.isContainerOpen(row), wasOpen, testid + "opened container node on edit");
   663   // ensure to restore editable attribute
   664   if (!wasEditable)
   665     tree.editable = false;
   667   var ci = tree.currentIndex;
   669   // cursor navigation should not change the selection while editing
   670   var testKey = function(key) {
   671     synthesizeKeyExpectEvent(key, {}, tree, "!select", "key " + key + " with editing");
   672     is(tree.editingRow == rowIndex && tree.editingColumn == ecolumn && tree.currentIndex == ci,
   673                   true, testid + "key " + key + " while editing");
   674   }
   676   testKey("VK_DOWN");
   677   testKey("VK_UP");
   678   testKey("VK_PAGE_DOWN");
   679   testKey("VK_PAGE_UP");
   680   testKey("VK_HOME");
   681   testKey("VK_END");
   683   // XXXndeakin figure out how to send characters to the textbox
   684   // inputField.inputField.focus()
   685   // synthesizeKeyExpectEvent(inputField.inputField, "b", null, "");
   686   // tree.stopEditing(true);
   687   // is(tree.view.getCellText(0, ecolumn), "b", testid + "edit cell");
   689   // Restore initial state.
   690   tree.stopEditing(false);
   691 }
   693 function testtag_tree_TreeSelection_UI_cell(tree, testid, rowInfo)
   694 {
   695   testid += " selection UI cell ";
   697   var columns = tree.columns;
   698   var firstcolumn = columns[0];
   699   var secondcolumn = columns[1];
   700   var lastcolumn = columns[columns.length - 1];
   701   var secondlastcolumn = columns[columns.length - 2];
   702   var selection = tree.view.selection;
   704   selection.clearSelection();
   705   selection.currentIndex = -1;
   706   selection.currentColumn = firstcolumn;
   707   is(selection.currentColumn, firstcolumn, testid + " first currentColumn");
   709   // no selection yet so nothing should happen when the left and right cursor keys are pressed
   710   synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right no selection");
   711   testtag_tree_TreeSelection_State(tree, testid + "key right no selection", -1, [], null, firstcolumn);
   713   selection.currentColumn = secondcolumn;
   714   synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left no selection");
   715   testtag_tree_TreeSelection_State(tree, testid + "key left no selection", -1, [], null, secondcolumn);
   717   selection.select(2);
   718   selection.currentIndex = 2;
   719   if (0) { // XXXndeakin disable these tests for now
   720     mouseOnCell(tree, 1, secondlastcolumn, "mouse on cell");
   721     testtag_tree_TreeSelection_State(tree, testid + "mouse on cell", 1, [1], null, secondlastcolumn);
   722   }
   724   tree.focus();
   726   // selection is set, so it should move when the left and right cursor keys are pressed
   727   tree.treeBoxObject.scrollToRow(0);
   728   selection.select(1);
   729   selection.currentIndex = 1;
   730   selection.currentColumn = secondcolumn;
   731   synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left in second column");
   732   testtag_tree_TreeSelection_State(tree, testid + "key left in second column", 1, [1], 0, firstcolumn);
   734   synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left in first column");
   735   testtag_tree_TreeSelection_State(tree, testid + "key left in first column", 1, [1], 0, firstcolumn);
   737   selection.currentColumn = secondlastcolumn;
   738   synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right in second last column");
   739   testtag_tree_TreeSelection_State(tree, testid + "key right in second last column", 1, [1], 0, lastcolumn);
   741   synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right in last column");
   742   testtag_tree_TreeSelection_State(tree, testid + "key right in last column", 1, [1], 0, lastcolumn);
   744   synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up in second row");
   745   testtag_tree_TreeSelection_State(tree, testid + "key up in second row", 0, [0], 0, lastcolumn);
   747   synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up in first row");
   748   testtag_tree_TreeSelection_State(tree, testid + "key up in first row", 0, [0], 0, lastcolumn);
   750   synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down in first row");
   751   testtag_tree_TreeSelection_State(tree, testid + "key down in first row", 1, [1], 0, lastcolumn);
   753   var lastidx = tree.view.rowCount - 1;
   754   tree.treeBoxObject.scrollToRow(lastidx - 3);
   755   selection.select(lastidx);
   756   selection.currentIndex = lastidx;
   757   synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down in last row");
   758   testtag_tree_TreeSelection_State(tree, testid + "key down in last row", lastidx, [lastidx], lastidx - 3, lastcolumn);
   760   synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home");
   761   testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0, lastcolumn);
   763   synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end");
   764   testtag_tree_TreeSelection_State(tree, testid + "key end", lastidx, [lastidx], lastidx - 3, lastcolumn);
   766   for (var t = 0; t < 2; t++) {
   767     var testidmod = (t == 0) ? "" : " rev";
   769     // scroll to the end, subtract 3 because we want lastidx to appear
   770     // at the end of view
   771     tree.treeBoxObject.scrollToRow(lastidx - 3);
   772     selection.select(lastidx);
   773     selection.currentIndex = lastidx;
   774     var expectedrow = tree.pageUpOrDownMovesSelection ? lastidx - 3 : lastidx;
   775     synthesizeKeyExpectEvent("VK_PAGE_UP", {}, tree, "!select", "key page up");
   776     testtag_tree_TreeSelection_State(tree, testid + "key page up" + testidmod,
   777                                      expectedrow, [expectedrow],
   778                                      tree.pageUpOrDownMovesSelection ? lastidx - 3 : 0, lastcolumn);
   780     tree.treeBoxObject.scrollToRow(1);
   781     selection.select(1);
   782     selection.currentIndex = 1;
   783     var expectedrow = tree.pageUpOrDownMovesSelection ? 4 : 1;
   784     synthesizeKeyExpectEvent("VK_PAGE_DOWN", {}, tree, "!select", "key page down");
   785     testtag_tree_TreeSelection_State(tree, testid + "key page down" + testidmod,
   786                                      expectedrow, [expectedrow],
   787                                      tree.pageUpOrDownMovesSelection ? 1 : lastidx - 3, lastcolumn);
   789     tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection;
   790   }
   792   // now check navigation when there is unselctable column
   793   secondcolumn.element.setAttribute("selectable", "false");
   794   secondcolumn.invalidate();
   795   is(secondcolumn.selectable, false, testid + "set selectable attribute");
   797   if (columns.length >= 3) {
   798     selection.select(3);
   799     selection.currentIndex = 3;
   800     // check whether unselectable columns are skipped over
   801     selection.currentColumn = firstcolumn;
   802     synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable column");
   803     testtag_tree_TreeSelection_State(tree, testid + "key right unselectable column",
   804                                      3, [3], null, secondcolumn.getNext());
   806     synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable column");
   807     testtag_tree_TreeSelection_State(tree, testid + "key left unselectable column",
   808                                      3, [3], null, firstcolumn);
   809   }
   811   secondcolumn.element.removeAttribute("selectable");
   812   secondcolumn.invalidate();
   813   is(secondcolumn.selectable, true, testid + "clear selectable attribute");
   815   // check to ensure that navigation isn't allowed if the first column is not selectable
   816   selection.currentColumn = secondcolumn;
   817   firstcolumn.element.setAttribute("selectable", "false");
   818   firstcolumn.invalidate();
   819   synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable first column");
   820   testtag_tree_TreeSelection_State(tree, testid + "key left unselectable first column",
   821                                    3, [3], null, secondcolumn);
   822   firstcolumn.element.removeAttribute("selectable");
   823   firstcolumn.invalidate();
   825   // check to ensure that navigation isn't allowed if the last column is not selectable
   826   selection.currentColumn = secondlastcolumn;
   827   lastcolumn.element.setAttribute("selectable", "false");
   828   lastcolumn.invalidate();
   829   synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable last column");
   830   testtag_tree_TreeSelection_State(tree, testid + "key right unselectable last column",
   831                                    3, [3], null, secondlastcolumn);
   832   lastcolumn.element.removeAttribute("selectable");
   833   lastcolumn.invalidate();
   835   // now check for cells with selectable false
   836   if (!rowInfo.rows[4].cells[1].selectable && columns.length >= 3) {
   837     // check whether unselectable cells are skipped over
   838     selection.select(4);
   839     selection.currentIndex = 4;
   841     selection.currentColumn = firstcolumn;
   842     synthesizeKeyExpectEvent("VK_RIGHT", {}, tree, "!select", "key right unselectable cell");
   843     testtag_tree_TreeSelection_State(tree, testid + "key right unselectable cell",
   844                                      4, [4], null, secondcolumn.getNext());
   846     synthesizeKeyExpectEvent("VK_LEFT", {}, tree, "!select", "key left unselectable cell");
   847     testtag_tree_TreeSelection_State(tree, testid + "key left unselectable cell",
   848                                      4, [4], null, firstcolumn);
   850     tree.treeBoxObject.scrollToRow(1);
   851     selection.select(3);
   852     selection.currentIndex = 3;
   853     selection.currentColumn = secondcolumn;
   855     synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down unselectable cell");
   856     testtag_tree_TreeSelection_State(tree, testid + "key down unselectable cell",
   857                                      5, [5], 2, secondcolumn);
   859     tree.treeBoxObject.scrollToRow(4);
   860     synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up unselectable cell");
   861     testtag_tree_TreeSelection_State(tree, testid + "key up unselectable cell",
   862                                      3, [3], 3, secondcolumn);
   863   }
   865   // restore the scroll position to the start of the page
   866   sendKey("HOME");
   867 }
   869 function testtag_tree_TreeView(tree, testid, rowInfo)
   870 {
   871   testid += " view ";
   873   var columns = tree.columns;
   874   var view = tree.view;
   876   is(view instanceof Components.interfaces.nsITreeView, true, testid + "view is a TreeView");
   877   is(view.rowCount, rowInfo.rows.length, testid + "rowCount");
   879   testtag_tree_TreeView_rows(tree, testid, rowInfo, 0);
   881   // note that this will only work for content trees currently
   882   view.setCellText(0, columns[1], "Changed Value");
   883   is(view.getCellText(0, columns[1]), "Changed Value", "setCellText");
   885   view.setCellValue(1, columns[0], "Another Changed Value");
   886   is(view.getCellValue(1, columns[0]), "Another Changed Value", "setCellText");
   887 }
   889 function testtag_tree_TreeView_rows(tree, testid, rowInfo, startRow)
   890 {
   891   var r;
   892   var columns = tree.columns;
   893   var view = tree.view;
   894   var length = rowInfo.rows.length;
   896   // methods to test along with the functions which determine the expected value
   897   var checkRowMethods =
   898   {
   899     isContainer: function(row) { return row.container },
   900     isContainerOpen: function(row) { return false },
   901     isContainerEmpty: function(row) { return (row.children != null && row.children.rows.length == 0) },
   902     isSeparator: function(row) { return row.separator },
   903     getRowProperties: function(row) { return row.properties },
   904     getLevel: function(row) { return row.level },
   905     getParentIndex: function(row) { return row.parent },
   906     hasNextSibling: function(row) { return r < startRow + length - 1; }
   907   };
   909   var checkCellMethods =
   910   {
   911     getCellText: function(row, cell) { return cell.label },
   912     getCellValue: function(row, cell) { return cell.value },
   913     getCellProperties: function(row, cell) { return cell.properties },
   914     isEditable: function(row, cell) { return cell.editable },
   915     isSelectable: function(row, cell) { return cell.selectable },
   916     getImageSrc: function(row, cell) { return cell.image },
   917     getProgressMode: function(row, cell) { return cell.mode }
   918   };
   920   var failedMethods = { };
   921   var checkMethod, actual, expected;
   922   var containerInfo = null;
   923   var toggleOpenStateOK = true;
   925   for (r = startRow; r < length; r++) {
   926     var row = rowInfo.rows[r];
   927     for (var c = 0; c < row.cells.length; c++) {
   928       var cell = row.cells[c];
   930       for (checkMethod in checkCellMethods) {
   931         expected = checkCellMethods[checkMethod](row, cell);
   932         actual = view[checkMethod](r, columns[c]);
   933         if (actual !== expected) {
   934           failedMethods[checkMethod] = true;
   935           is(actual, expected, testid + "row " + r + " column " + c + " " + checkMethod + " is incorrect");
   936         }
   937       }
   938     }
   940     // compare row properties
   941     for (checkMethod in checkRowMethods) {
   942       expected = checkRowMethods[checkMethod](row, r);
   943       if (checkMethod == "hasNextSibling") {
   944         actual = view[checkMethod](r, r);
   945       }
   946       else {
   947         actual = view[checkMethod](r);
   948       }
   949       if (actual !== expected) {
   950         failedMethods[checkMethod] = true;
   951         is(actual, expected, testid + "row " + r + " " + checkMethod + " is incorrect");
   952       }
   953     }
   954 /*
   955     // open and recurse into containers
   956     if (row.container) {
   957       view.toggleOpenState(r);
   958       if (!view.isContainerOpen(r)) {
   959         toggleOpenStateOK = false;
   960         is(view.isContainerOpen(r), true, testid + "row " + r + " toggleOpenState open");
   961       }
   962       testtag_tree_TreeView_rows(tree, testid + "container " + r + " ", row.children, r + 1);
   963       view.toggleOpenState(r);
   964       if (view.isContainerOpen(r)) {
   965         toggleOpenStateOK = false;
   966         is(view.isContainerOpen(r), false, testid + "row " + r + " toggleOpenState close");
   967       }
   968     }
   969 */
   970   }
   972   for (var failedMethod in failedMethods) {
   973     if (failedMethod in checkRowMethods)
   974       delete checkRowMethods[failedMethod];
   975     if (failedMethod in checkCellMethods)
   976       delete checkCellMethods[failedMethod];
   977   }
   979   for (checkMethod in checkRowMethods)
   980     is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod);
   981   for (checkMethod in checkCellMethods)
   982     is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod);
   983   if (toggleOpenStateOK)
   984     is("toggleOpenState ok", "toggleOpenState ok", testid + "toggleOpenState");
   985 }
   987 function testtag_tree_TreeView_rows_sort(tree, testid, rowInfo)
   988 {
   989   // check if cycleHeader sorts the columns
   990   var columnIndex = 0;
   991   var view = tree.view;
   992   var column = tree.columns[columnIndex];
   993   var columnElement = column.element;
   994   var sortkey = columnElement.getAttribute("sort");
   995   if (sortkey) {
   996     view.cycleHeader(column);
   997     is(tree.getAttribute("sort"), sortkey, "cycleHeader sort");
   998     is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending");
   999     is(columnElement.getAttribute("sortDirection"), "ascending", "cycleHeader column sortDirection");
  1000     is(columnElement.getAttribute("sortActive"), "true", "cycleHeader column sortActive");
  1001     view.cycleHeader(column);
  1002     is(tree.getAttribute("sortDirection"), "descending", "cycleHeader sortDirection descending");
  1003     is(columnElement.getAttribute("sortDirection"), "descending", "cycleHeader column sortDirection descending");
  1004     view.cycleHeader(column);
  1005     is(tree.getAttribute("sortDirection"), "", "cycleHeader sortDirection natural");
  1006     is(columnElement.getAttribute("sortDirection"), "", "cycleHeader column sortDirection natural");
  1007     // XXXndeakin content view isSorted needs to be tested
  1010   // Check that clicking on column header sorts the column.
  1011   var columns = getSortedColumnArray(tree);
  1012   is(columnElement.getAttribute("sortDirection"), "",
  1013      "cycleHeader column sortDirection");
  1015   // Click once on column header and check sorting has cycled once.
  1016   mouseClickOnColumnHeader(columns, columnIndex, 0, 1);
  1017   is(columnElement.getAttribute("sortDirection"), "ascending",
  1018      "single click cycleHeader column sortDirection ascending");
  1020   // Now simulate a double click.
  1021   mouseClickOnColumnHeader(columns, columnIndex, 0, 2);
  1022   if (navigator.platform.indexOf("Win") == 0) {
  1023     // Windows cycles only once on double click.
  1024     is(columnElement.getAttribute("sortDirection"), "descending",
  1025        "double click cycleHeader column sortDirection descending");
  1026     // 1 single clicks should restore natural sorting.
  1027     mouseClickOnColumnHeader(columns, columnIndex, 0, 1);
  1030   // Check we have gone back to natural sorting.
  1031   is(columnElement.getAttribute("sortDirection"), "",
  1032      "cycleHeader column sortDirection");
  1034   columnElement.setAttribute("sorthints", "twostate");
  1035   view.cycleHeader(column);
  1036   is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending twostate");
  1037   view.cycleHeader(column);
  1038   is(tree.getAttribute("sortDirection"), "descending", "cycleHeader sortDirection ascending twostate");
  1039   view.cycleHeader(column);
  1040   is(tree.getAttribute("sortDirection"), "ascending", "cycleHeader sortDirection ascending twostate again");
  1041   columnElement.removeAttribute("sorthints");
  1042   view.cycleHeader(column);
  1043   view.cycleHeader(column);
  1045   is(columnElement.getAttribute("sortDirection"), "",
  1046      "cycleHeader column sortDirection reset");
  1049 // checks if the current and selected rows are correct
  1050 // current is the index of the current row
  1051 // selected is an array of the indicies of the selected rows
  1052 // column is the selected column
  1053 // viewidx is the row that should be visible at the top of the tree
  1054 function testtag_tree_TreeSelection_State(tree, testid, current, selected, viewidx, column)
  1056   var selection = tree.view.selection;
  1058   if (!column)
  1059     column = (tree.selType == "cell") ? tree.columns[0] : null;
  1061   is(selection.count, selected.length, testid + " count");
  1062   is(tree.currentIndex, current, testid + " currentIndex");
  1063   is(selection.currentIndex, current, testid + " TreeSelection currentIndex");
  1064   is(selection.currentColumn, column, testid + " currentColumn");
  1065   if (viewidx !== null && viewidx !== undefined)
  1066     is(tree.treeBoxObject.getFirstVisibleRow(), viewidx, testid + " first visible row");
  1068   var actualSelected = [];
  1069   var count = tree.view.rowCount;
  1070   for (var s = 0; s < count; s++) {
  1071     if (selection.isSelected(s))
  1072       actualSelected.push(s);
  1075   is(compareArrays(selected, actualSelected), true, testid + " selection [" + selected + "]");
  1077   actualSelected = [];
  1078   var rangecount = selection.getRangeCount();
  1079   for (var r = 0; r < rangecount; r++) {
  1080     var start = {}, end = {};
  1081     selection.getRangeAt(r, start, end);
  1082     for (var rs = start.value; rs <= end.value; rs++)
  1083       actualSelected.push(rs);
  1086   is(compareArrays(selected, actualSelected), true, testid + " range selection [" + selected + "]");
  1089 function testtag_tree_column_reorder()
  1091   // Make sure the tree is scrolled into the view, otherwise the test will
  1092   // fail
  1093   var testframe = window.parent.document.getElementById("testframe");
  1094   if (testframe) {
  1095     testframe.scrollIntoView();
  1098   var tree = document.getElementById("tree-column-reorder");
  1099   var numColumns = tree.columns.count;
  1101   var reference = [];
  1102   for (var i = 0; i < numColumns; i++) {
  1103     reference.push("col_" + i);
  1106   // Drag the first column to each position
  1107   for (var i = 0; i < numColumns - 1; i++) {
  1108     synthesizeColumnDrag(tree, i, i + 1, true);
  1109     arrayMove(reference, i, i + 1, true);
  1110     checkColumns(tree, reference, "drag first column right");
  1113   // And back
  1114   for (var i = numColumns - 1; i >= 1; i--) {
  1115     synthesizeColumnDrag(tree, i, i - 1, false);
  1116     arrayMove(reference, i, i - 1, false);
  1117     checkColumns(tree, reference, "drag last column left");
  1120   // Drag each column one column left
  1121   for (var i = 1; i < numColumns; i++) {
  1122     synthesizeColumnDrag(tree, i, i - 1, false);
  1123     arrayMove(reference, i, i - 1, false);
  1124     checkColumns(tree, reference, "drag each column left");
  1127   // And back
  1128   for (var i = numColumns - 2; i >= 0; i--) {
  1129     synthesizeColumnDrag(tree, i, i + 1, true);
  1130     arrayMove(reference, i, i + 1, true);
  1131     checkColumns(tree, reference, "drag each column right");
  1134   // Drag each column 5 to the right
  1135   for (var i = 0; i < numColumns - 5; i++) {
  1136     synthesizeColumnDrag(tree, i, i + 5, true);
  1137     arrayMove(reference, i, i + 5, true);
  1138     checkColumns(tree, reference, "drag each column 5 to the right");
  1141   // And to the left
  1142   for (var i = numColumns - 6; i >= 5; i--) {
  1143     synthesizeColumnDrag(tree, i, i - 5, false);
  1144     arrayMove(reference, i, i - 5, false);
  1145     checkColumns(tree, reference, "drag each column 5 to the left");
  1148   // Test that moving a column after itself does not move anything
  1149   synthesizeColumnDrag(tree, 0, 0, true);
  1150   checkColumns(tree, reference, "drag to itself");
  1151   is(document.treecolDragging, null, "drag to itself completed");
  1153   // XXX roc should this be here???
  1154   SimpleTest.finish();
  1157 function testtag_tree_wheel(aTree)
  1159   const deltaModes = [
  1160     WheelEvent.DOM_DELTA_PIXEL,  // 0
  1161     WheelEvent.DOM_DELTA_LINE,   // 1
  1162     WheelEvent.DOM_DELTA_PAGE    // 2
  1163   ];
  1164   function helper(aStart, aDelta, aIntDelta, aDeltaMode)
  1166     aTree.treeBoxObject.scrollToRow(aStart);
  1167     var expected = !aIntDelta ? aStart :
  1168           aDeltaMode != WheelEvent.DOM_DELTA_PAGE ? aStart + aIntDelta :
  1169           aIntDelta > 0 ? aStart + aTree.treeBoxObject.getPageLength() :
  1170                           aStart - aTree.treeBoxObject.getPageLength();
  1171     if (expected < 0) {
  1172       expected = 0;
  1174     if (expected > aTree.view.rowCount - aTree.treeBoxObject.getPageLength()) {
  1175       expected = aTree.view.rowCount - aTree.treeBoxObject.getPageLength();
  1177     synthesizeWheel(aTree.body, 1, 1,
  1178                     { deltaMode: aDeltaMode, deltaY: aDelta,
  1179                       lineOrPageDeltaY: aIntDelta });
  1180     is(aTree.treeBoxObject.getFirstVisibleRow(), expected,
  1181          "testtag_tree_wheel: vertical, starting " + aStart +
  1182            " delta " + aDelta + " lineOrPageDelta " + aIntDelta +
  1183            " aDeltaMode " + aDeltaMode);
  1185     aTree.treeBoxObject.scrollToRow(aStart);
  1186     // Check that horizontal scrolling has no effect
  1187     synthesizeWheel(aTree.body, 1, 1,
  1188                     { deltaMode: aDeltaMode, deltaX: aDelta,
  1189                       lineOrPageDeltaX: aIntDelta });
  1190     is(aTree.treeBoxObject.getFirstVisibleRow(), aStart,
  1191          "testtag_tree_wheel: horizontal, starting " + aStart +
  1192            " delta " + aDelta + " lineOrPageDelta " + aIntDelta +
  1193            " aDeltaMode " + aDeltaMode);
  1196   var defaultPrevented = 0;
  1198   function wheelListener(event) {
  1199     defaultPrevented++;
  1201   window.addEventListener("wheel", wheelListener, false);
  1203   deltaModes.forEach(function(aDeltaMode) {
  1204     var delta = (aDeltaMode == WheelEvent.DOM_DELTA_PIXEL) ? 5.0 : 0.3;
  1205     helper(2, -delta,  0, aDeltaMode);
  1206     helper(2, -delta, -1, aDeltaMode);
  1207     helper(2,  delta,  0, aDeltaMode);
  1208     helper(2,  delta,  1, aDeltaMode);
  1209     helper(2, -2 * delta,  0, aDeltaMode);
  1210     helper(2, -2 * delta, -1, aDeltaMode);
  1211     helper(2,  2 * delta,  0, aDeltaMode);
  1212     helper(2,  2 * delta,  1, aDeltaMode);
  1213   });
  1215   window.removeEventListener("wheel", wheelListener, false);
  1216   is(defaultPrevented, 48, "wheel event default prevented");
  1219 function synthesizeColumnDrag(aTree, aMouseDownColumnNumber, aMouseUpColumnNumber, aAfter)
  1221   var columns = getSortedColumnArray(aTree);
  1223   var down = columns[aMouseDownColumnNumber].element;
  1224   var up   = columns[aMouseUpColumnNumber].element;
  1226   // Target the initial mousedown in the middle of the column header so we
  1227   // avoid the extra hit test space given to the splitter
  1228   var columnWidth = down.boxObject.width;
  1229   var splitterHitWidth = columnWidth / 2;
  1230   synthesizeMouse(down, splitterHitWidth, 3, { type: "mousedown"});
  1232   var offsetX = 0;
  1233   if (aAfter) {
  1234     offsetX = columnWidth;
  1237   if (aMouseUpColumnNumber > aMouseDownColumnNumber) {
  1238     for (var i = aMouseDownColumnNumber; i <= aMouseUpColumnNumber; i++) {
  1239       var move = columns[i].element;
  1240       synthesizeMouse(move, offsetX, 3, { type: "mousemove"});
  1243   else {
  1244     for (var i = aMouseDownColumnNumber; i >= aMouseUpColumnNumber; i--) {
  1245       var move = columns[i].element;
  1246       synthesizeMouse(move, offsetX, 3, { type: "mousemove"});
  1250   synthesizeMouse(up, offsetX, 3, { type: "mouseup"});
  1253 function arrayMove(aArray, aFrom, aTo, aAfter)
  1255   var o = aArray.splice(aFrom, 1)[0];
  1256   if (aTo > aFrom) {
  1257     aTo--;
  1260   if (aAfter) {
  1261     aTo++;
  1264   aArray.splice(aTo, 0, o);
  1267 function getSortedColumnArray(aTree)
  1269   var columns = aTree.columns;
  1270   var a = [];
  1271   for (var i = 0; i < columns.length; i++) {
  1272     a.push(columns.getColumnAt(i));
  1275   a.sort(function(a, b) {
  1276     var o1 = parseInt(a.element.getAttribute("ordinal"));
  1277     var o2 = parseInt(b.element.getAttribute("ordinal"));
  1278     return o1 - o2;
  1279   });
  1280   return a;
  1283 function checkColumns(aTree, aReference, aMessage)
  1285   var columns = getSortedColumnArray(aTree);
  1286   var ids = [];
  1287   columns.forEach(function(e) {
  1288     ids.push(e.element.id);
  1289   });
  1290   is(compareArrays(ids, aReference), true, aMessage);
  1293 function mouseOnCell(tree, row, column, testname)
  1295   var x = {}, y = {}, width = {}, height = {};
  1296   tree.boxObject.getCoordsForCellItem(row, column, "text", x, y, width, height);
  1298   synthesizeMouseExpectEvent(tree.body, x.value, y.value, {}, tree, "select", testname);
  1301 function mouseClickOnColumnHeader(aColumns, aColumnIndex, aButton, aClickCount)
  1303   var columnHeader = aColumns[aColumnIndex].element;
  1304   var columnHeaderRect = columnHeader.getBoundingClientRect();
  1305   var columnWidth = columnHeaderRect.right - columnHeaderRect.left;
  1306   // For multiple click we send separate click events, with increasing
  1307   // clickCount.  This simulates the common behavior of multiple clicks.
  1308   for (var i = 1; i <= aClickCount; i++) {
  1309     // Target the middle of the column header.
  1310     synthesizeMouse(columnHeader, columnWidth / 2, 3,
  1311                     { button: aButton,
  1312                       clickCount: i }, null);
  1316 function mouseDblClickOnCell(tree, row, column, testname)
  1318   // select the row we will edit
  1319   var selection = tree.view.selection;
  1320   selection.select(row);
  1321   tree.treeBoxObject.ensureRowIsVisible(row);
  1323   // get cell coordinates
  1324   var x = {}, y = {}, width = {}, height = {};
  1325   tree.treeBoxObject.getCoordsForCellItem(row, column, "text", x, y, width, height);
  1327   synthesizeMouse(tree.body, x.value, y.value, { clickCount: 2 }, null);
  1330 function compareArrays(arr1, arr2)
  1332   if (arr1.length != arr2.length)
  1333     return false;
  1335   for (var i = 0; i < arr1.length; i++) {
  1336     if (arr1[i] != arr2[i])
  1337       return false;
  1340   return true;
  1343 function convertProperties(arr)
  1345   var results = [];
  1346   var count = arr.Count();
  1347   for (var i = 0; i < count; i++)
  1348     results.push(arr.GetElementAt(i).QueryInterface(Components.interfaces.nsIAtom).toString());
  1350   results.sort();
  1351   return results.join(" ");
  1354 function convertDOMtoTreeRowInfo(treechildren, level, rowidx)
  1356   var obj = { rows: [] };
  1358   var parentidx = rowidx.value;
  1360   treechildren = treechildren.childNodes;
  1361   for (var r = 0; r < treechildren.length; r++) {
  1362     rowidx.value++;
  1364     var treeitem = treechildren[r];
  1365     if (treeitem.hasChildNodes()) {
  1366       var treerow = treeitem.firstChild;
  1367       var cellInfo = [];
  1368       for (var c = 0; c < treerow.childNodes.length; c++) {
  1369         var cell = treerow.childNodes[c];
  1370         cellInfo.push({ label: "" + cell.getAttribute("label"),
  1371                         value: cell.getAttribute("value"),
  1372                         properties: cell.getAttribute("properties"),
  1373                         editable: cell.getAttribute("editable") != "false",
  1374                         selectable: cell.getAttribute("selectable") != "false",
  1375                         image: cell.getAttribute("src"),
  1376                         mode: cell.hasAttribute("mode") ? parseInt(cell.getAttribute("mode")) : 3 });
  1379       var descendants = treeitem.lastChild;
  1380       var children = (treerow == descendants) ? null :
  1381                      convertDOMtoTreeRowInfo(descendants, level + 1, rowidx);
  1382       obj.rows.push({ cells: cellInfo,
  1383                       properties: treerow.getAttribute("properties"),
  1384                       container: treeitem.getAttribute("container") == "true",
  1385                       separator: treeitem.localName == "treeseparator",
  1386                       children: children,
  1387                       level: level,
  1388                       parent: parentidx });
  1392   return obj;

mercurial