<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                 type="text/css"?>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="nsIAccessibleTable interface on xul:listbox test.">

  <script type="application/javascript" 
          src="chrome://mochikit/content/MochiKit/packed.js"></script>
  <script type="application/javascript"
          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
  <script type="application/javascript">
  <![CDATA[
    var gAccService = null;
    var Ci = Components.interfaces;

    function doTest()
    {
      gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
                    getService(Components.interfaces.nsIAccessibleRetrieval);
      var id = "";
      var listbox = null, acc = null;

      //////////////////////////////////////////////////////////////////////////
      // Simple listbox. There is no nsIAccessibleTable interface.

      id = "listbox1";
      listbox = document.getElementById(id);
      acc = gAccService.getAccessibleFor(listbox);

      // query nsIAccessibleTable
      try {
        acc.QueryInterface(Ci.nsIAccessibleTable);
        ok(false,
           id + " shouldn't implement nsIAccessibleTable interface.");
      } catch(e) {
        ok(true, id + " doesn't implement nsIAccessibleTable interface.");
      }

      // role
      is(acc.role, Ci.nsIAccessibleRole.ROLE_LISTBOX,
         id + " has wrong role.");

      //////////////////////////////////////////////////////////////////////////
      // Multicolumn listbox.

      id = "listbox2";
      listbox = document.getElementById(id);
      acc = gAccService.getAccessibleFor(listbox);

      // role
      is(acc.role, Ci.nsIAccessibleRole.ROLE_TABLE,
         id + " has wrong role.");

      // query nsIAccessibleTable
      try {
        acc.QueryInterface(Ci.nsIAccessibleTable);
        ok(true,
           id + " implements nsIAccessibleTable interface.");
      } catch(e) {
        ok(false, id + " doesn't implement nsIAccessibleTable interface.");
      }

      // rows count
      var rowsCount = acc.rows;
      is (rowsCount, 2, id + " has wrong number of rows.");

      // columns count
      var colsCount = acc.columns;
      is (colsCount, 2, id + " has wrong number of columns.");

      //////////////////////////////////////////////////////////////////////////
      // Multicolumn listbox with header.

      id = "listbox3";
      listbox = document.getElementById(id);
      acc = gAccService.getAccessibleFor(listbox);

      // role
      is(acc.role, Ci.nsIAccessibleRole.ROLE_TABLE,
         id + " has wrong role.");

      // query nsIAccessibleTable
      try {
        acc.QueryInterface(Ci.nsIAccessibleTable);
        ok(true,
           id + " implements nsIAccessibleTable interface.");
      } catch(e) {
        ok(false, id + " doesn't implement nsIAccessibleTable interface.");
      }

      // rows count
      var rowsCount = acc.rows;
      is (rowsCount, 3, id + " has wrong number of rows.");

      // columns count
      var colsCount = acc.columns;
      is (colsCount, 3, id + " has wrong number of columns.");

      // getCellRefAt, getIndexAt, getRowAtIndex, getColumnAtIndex,
      // getColumnExtentAt, getRowExtentAt.
      for (var row = 0; row < rowsCount; row++) {
        for (var col = 0; col < colsCount; col++) {
          var index = row * colsCount + col;

          is(acc.cellRefAt(row, col).name, "cell" + index,
             id + ": wrong cell returned for (" + row + ", " + col + ")");

          is(acc.getIndexAt(row, col), index,
             id + ": wrong index returned for (" + row + ", " + col + ")");
          is(acc.getRowAtIndex(index), row,
             id + ": wrong row at the index " + index);
          is(acc.getColumnAtIndex(index), col,
             id + ": wrong column at the index " + index);

          is(acc.getColumnExtentAt(row, col), 1,
             id + ": colspan wrong for (" + row + ", " + col + ")");
          is(acc.getRowExtentAt(row, col), 1,
             id + ": rowspan wrong for (" + row + ", " + col + ")");
        }
      }

      // columns selection
      testColumnSelection(id, acc, colsCount, 0, null);
      acc.selectColumn(0);
      testColumnSelection(id, acc, colsCount, 0, null);

      // rows selection
      testRowSelection(id, acc, rowsCount, 0, null);
      acc.selectRow(0);
      testRowSelection(id, acc, rowsCount, 1, [0]);
      acc.selectRow(1);
      testRowSelection(id, acc, rowsCount, 1, [1]);
      acc.unselectRow(1);
      testRowSelection(id, acc, rowsCount, 0, null);

      // cells selection
      testCellSelection(id, acc, rowsCount, colsCount, 0, null);
      acc.selectRow(2);
      testCellSelection(id, acc, rowsCount, colsCount, 3, [6, 7, 8]);
      acc.unselectRow(2);
      testCellSelection(id, acc, rowsCount, colsCount, 0, null);

      SimpleTest.finish();
    }

    /**
     * Helper function to test isColumnSelected(), selectedColumnCount() and
     * getSelectedColumn() methods.
     */
    function testColumnSelection(aId, aAcc, aCount, aSelCount, aSelIndexesArray)
    {
      // isColumnSelected
      for (var col = 0; col < aCount; col++) {
        if (aSelIndexesArray && aSelIndexesArray.indexOf(col) != -1) {
          is(aAcc.isColumnSelected(col), true,
             aId + ": column " + col + " should be selected");
        } else {
          is(aAcc.isColumnSelected(col), false,
             aId + ": column " + col + " shouldn't be selected");
        }
      }

      // selectedColumnsCount
      is(aAcc.selectedColumnsCount, aSelCount,
         aId + ": wrong number of selected columns");

      // getSelectedColumns
      var selColsCount = {}, selCols = {};
      aAcc.getSelectedColumns(selColsCount, selCols);

      is(selColsCount.value, aSelCount,
         aId + ": wrong number of selected columns");

      if (!aSelIndexesArray) {
        is(selCols.value, null,
           aId + ": no columns should be selected");
      } else {
        for (var i = 0; i < selCols.length; i++) {
          is(selCols[i], aSelIndexesArray[i],
             aId + ": wrong selected column index " + i);
        }
      }
    }

    /**
     * Helper function to test isRowSelected(), selectedRowCount() and
     * getSelectedRow() methods.
     */
    function testRowSelection(aId, aAcc, aCount, aSelCount, aSelIndexesArray)
    {
      // isRowSelected
      for (var row = 0; row < aCount; row++) {
        if (aSelIndexesArray && aSelIndexesArray.indexOf(row) != -1) {
          is(aAcc.isRowSelected(row), true,
             aId + ": row " + row + " should be selected");
        } else {
          is(aAcc.isRowSelected(row), false,
             aId + ": row " + row + " shouldn't be selected");
        }
      }

      // selectedRowsCount
      is(aAcc.selectedRowsCount, aSelCount,
         aId + ": wrong number of selected rows");

      // getSelectedRows
      var selColsCount = {}, selCols = {};
      aAcc.getSelectedRows(selColsCount, selCols);

      is(selColsCount.value, aSelCount,
         aId + ": wrong number of selected rows");

      if (!aSelIndexesArray) {
        is(selCols.value, null,
           aId + ": no row should be selected");
      } else {
        for (var i = 0; i < selCols.length; i++) {
          is(selCols[i], aSelIndexesArray[i],
             aId + ": wrong selected row index " + i);
        }
      }
    }

    /**
     * Helper function to test isCellSelected(), selectedCellCount() and
     * getSelectedCells() methods.
     */
    function testCellSelection(aId, aAcc, aRowCount, aColCount,
                               aSelCount, aSelIndexesArray)
    {
      // isCellSelected
      for (var row = 0; row < aRowCount; row++) {
        for (var col = 0; col < aColCount; col++) {
          var index = aAcc.getIndexAt(row, col);
          if (aSelIndexesArray && aSelIndexesArray.indexOf(index) != -1) {
            is(aAcc.isCellSelected(row, col), true,
               aId + ": cell (" + row + ", " + col + ") should be selected");
          } else {
            is(aAcc.isCellSelected(row, col), false,
               aId + ": cell (" + row + ", " + col + ") shouldn't be selected");
          }
        }
      }

      // selectedCellCount
      is(aAcc.selectedCellsCount, aSelCount,
         aId + ": wrong number of selected cells");

      // getSelectedCells
      var selColsCount = {}, selCols = {};
      aAcc.getSelectedCells(selColsCount, selCols);

      is(selColsCount.value, aSelCount,
         aId + ": wrong number of selected cells");

      if (!aSelIndexesArray) {
        is(selCols.value, null,
           aId + ": no cells should be selected");
      } else {
        for (var i = 0; i < selCols.length; i++) {
          is(selCols[i], aSelIndexesArray[i],
             aId + ": wrong selected cell index " + i);
        }
      }
    }

    SimpleTest.waitForExplicitFinish();
    addLoadEvent(doTest);
  ]]>
  </script>

  <hbox style="overflow: auto;">
  <body xmlns="http://www.w3.org/1999/xhtml">
    <a target="_blank"
       href="https://bugzilla.mozilla.org/show_bug.cgi?id=418371"
       title="implement the rest of methods of nsIAccessibleTable on xul:listbox">
      Mozilla Bug 418371
    </a>
    <p id="display"></p>
    <div id="content" style="display: none">
    </div>
    <pre id="test">
    </pre>
  </body>

  <vbox flex="1">
    <label control="listbox1" value="listbox: "/>
    <listbox id="listbox1">
      <listitem label="item1" id="item1"/>
      <listitem label="item2" id="item2"/>
    </listbox>

    <label control="listbox2" value="multicolumn listbox: "/>
    <listbox id="listbox2">
      <listcols>
        <listcol flex="1"/>
        <listcol flex="1"/>
      </listcols>
      <listitem>
        <listcell label="cell1"/>
        <listcell label="cell2"/>
      </listitem>
      <listitem>
        <listcell label="cell1"/>
        <listcell label="cell2"/>
      </listitem>
    </listbox>

    <label control="listbox3" value="multicolumn listbox with header"/>
    <listbox id="listbox3">
      <listhead>
        <listheader label="header1"/>
        <listheader label="header2"/>
        <listheader label="header3"/>
      </listhead>
      <listcols>
        <listcol flex="1"/>
        <listcol flex="1"/>
        <listcol flex="1"/>
      </listcols>
      <listitem>
        <listcell label="cell0"/>
        <listcell label="cell1"/>
        <listcell label="cell2"/>
      </listitem>
      <listitem>
        <listcell label="cell3"/>
        <listcell label="cell4"/>
        <listcell label="cell5"/>
      </listitem>
      <listitem>
        <listcell label="cell6"/>
        <listcell label="cell7"/>
        <listcell label="cell8"/>
      </listitem>
    </listbox>
  </vbox>
  </hbox>

</window>

