<?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"?>

<!--
  Bug 368835 - fire TreeViewChanged/TreeRowCountChanged events.
  Bug 308564 - no accessibility events when data in a tree row changes.
-->

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="Mozilla Bug 368835">

  <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[
    function inTreeView() { }

    inTreeView.prototype =
    {
      mRowCount: 0,
      mTree: null,
      mData: {},

      get rowCount() { return this.mRowCount; },
      setTree: function(aTree) { this.mTree = aTree; },
      getCellText: function(aRow, aCol)
      {
        var key = String(aRow)  + aCol.id;
        if (key in this.mData)
          return this.mData[key];

        return "hello";
      },
      getRowProperties: function(aIndex, aProperties) {},
      getCellProperties: function(aIndex, aCol, aProperties) {},
      getColumnProperties: function(aCol, aProperties) {},
      getParentIndex: function(aRowIndex) { },
      hasNextSibling: function(aRowIndex, aAfterIndex) { },
      getLevel: function(aIndex) {},
      getImageSrc: function(aRow, aCol) {},
      getProgressMode: function(aRow, aCol) {},
      getCellValue: function(aRow, aCol) {},
      isContainer: function(aIndex) {},
      isContainerOpen: function(aIndex) {},
      isContainerEmpty: function(aIndex) {},
      isSeparator: function(aIndex) {},
      isSorted: function() {},
      toggleOpenState: function(aIndex) {},
      selectionChanged: function() {},
      cycleHeader: function(aCol) {},
      cycleCell: function(aRow, aCol) {},
      isEditable: function(aRow, aCol) {},
      isSelectable: function(aRow, aCol) {},
      setCellValue: function(aRow, aCol, aValue) {},
      setCellText: function(aRow, aCol, aValue) { },
      performAction: function(aAction) {},
      performActionOnRow: function(aAction, aRow) {},
      performActionOnCell: function(aAction, aRow, aCol) {}
    };

    var gTreeViewChangedCount = 0;
    var gTreeViewChanged = false;
    function TreeViewChangedHandler(aEvent)
    {
      gTreeViewChangedCount++;

      // We get two systems 'treeViewChanged' event when tree is initialized
      // The third one is our when we change the tree view by
      // nsITreeBoxObject::setTree.
      if (gTreeViewChangedCount == 3) {
        gTreeViewChanged = true;

        // Tree view has been setted. We can continue tests for the tree.
        window.setTimeout(doTest2, 500);
      }
    }

    var gTreeRowCountChanged = false;
    function TreeRowCountChangedHandler(aEvent)
    {
      gTreeRowCountChanged = true;

      var index = aEvent.getData("index");
      is(index, 0, "Wrong 'index' data of 'treeRowCountChanged' event.");

      var count = aEvent.getData("count");
      is(count, 1, "Wrong 'count' data of 'treeRowCountChanged' event.");
    }

    var gTreeColumnInvalidated = false;
    var gTreeRowInvalidated = false;

    var gTreeInvalidatedCount = 0;
    function TreeInvalidatedHandler(aEvent)
    {
      gTreeInvalidatedCount++;

      switch (gTreeInvalidatedCount) {
        case 1:
          TreeInvalidatedHandlerHelper(aEvent, 0, 5, null, null,
                                       "nsITreeBoxObject::rowCountChanged");
          break;
        case 2:
// XXX see bug 454647          TreeInvalidatedHandlerHelper(aEvent, null, null, 0, 0,
//                                       "nsITreeBoxObject::invalidateColumn");
          gTreeColumnInvalidated = true;
          break;
        case 3:
// XXX see bug 454647          TreeInvalidatedHandlerHelper(aEvent, 1, 1, null, null,
//                                       "nsITreeBoxObject::invalidateRow");
          gTreeRowInvalidated = true;
          break;
        }
    }

    function TreeInvalidatedHandlerHelper(aEvent, aStartRow, aEndRow,
                                          aStartCol, aEndCol, aCauseMsg)
    {
      var startRow = aEvent.getData("startrow");
      is(startRow, aStartRow,
         "Wrong 'startrow' of 'treeInvalidated' event on " + aCauseMsg);

      var endRow = aEvent.getData("endrow");
      is(endRow, aEndRow,
         "Wrong 'endrow' of 'treeInvalidated' event on " + aCauseMsg);

      var startCol = aEvent.getData("startcolumn");
      is(startCol, aStartCol,
         "Wrong 'startcolumn' of 'treeInvalidated' event on " + aCauseMsg);

      var endCol = aEvent.getData("endcolumn");
      is(endCol, aEndCol,
         "Wrong 'endcolumn' of 'treeInvalidated' event on " + aCauseMsg);
    }

    var gNameChangedOnTreeRowInvalidated = false;
    var gNameChangedOnTreeColumnInvalidated = false;

    var gA11yEventObserver = {
      observe: function observe(aSubject, aTopic, aData)
      {
        if (aTopic != "accessible-event")
          return;

        const nsIAccessibleEvent = Components.interfaces.nsIAccessibleEvent;
        var event = aSubject.QueryInterface(nsIAccessibleEvent);

        if (event.eventType != nsIAccessibleEvent.EVENT_NAME_CHANGE)
          return;

        ++this.mCount;

        // We should get first six 'name changed' events on
        //  nsITreeBoxObject::invalidateColumn when we update 0th column
        // containing six rows.
        if (this.mCount == 6) {
          gNameChangedOnTreeColumnInvalidated = true;

          // Make sure 'name change' events have been fired on
          // InvalidateColumn() before continue the test.
          window.setTimeout(doTest3, 0);
        }
        else if (this.mCount == 7)
          gNameChangedOnTreeRowInvalidated = true;
      },

      mCount: 0
    };

    function CheckEvents()
    {
      // If these fail then it doesn't mean actually events are not fired,
      // possibly setTimeout was executed earlier than events have been fired.

      // nsITreeBoxObject::view
      ok(gTreeViewChanged,
         "TreeViewChanged event should have been fired.");

      // nsITreeBoxObject::rowCountChanged
      ok(gTreeRowCountChanged,
         "TreeRowCountChanged event should have been fired.");

      // nsITreeBoxObject::invalidateColumn
      ok(gTreeColumnInvalidated,
         "TreeInvalidated event should have been fired for InvalidateColumn().");
      ok(gNameChangedOnTreeColumnInvalidated,
         "Wrong NameChanged events number on tree column invalidation.");

      // nsITreeBoxObject::invalidateRow
      ok(gTreeRowInvalidated,
         "TreeInvalidated event should have been fired for InvalidateRow().");
      ok(gNameChangedOnTreeRowInvalidated,
         "Wrong NameChanged events number on tree row invalidation.");

      // Remove DOM event listeners
      document.removeEventListener("TreeViewChanged",
                                   TreeViewChangedHandler, true);

      document.removeEventListener("TreeRowCountChanged",
                                   TreeRowCountChangedHandler, true);

      document.removeEventListener("TreeInvalidated",
                                   TreeInvalidatedHandler, true);

      // Remove a11y events listener
      gObserverService.removeObserver(gA11yEventObserver, "accessible-event");

      SimpleTest.finish();
    }

    var gAccService = null;
    var gObserverService = null;

    var gTree = null;
    var gTreeBox = null;
    var gTreeView = null;

    function doTest()
    {
      // Activate accessibility, otherwise events aren't fired.
      gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
                    getService(Components.interfaces.nsIAccessibleRetrieval);

      // Add accessibility event listeners
      gObserverService = Components.classes["@mozilla.org/observer-service;1"].
                         getService(Components.interfaces.nsIObserverService);
      gObserverService.addObserver(gA11yEventObserver, "accessible-event",
                                   false);
      // Add DOM event listeners
      document.addEventListener("TreeViewChanged",
                                TreeViewChangedHandler, true);
      document.addEventListener("TreeRowCountChanged",
                                TreeRowCountChangedHandler, true);
      document.addEventListener("TreeInvalidated",
                                TreeInvalidatedHandler, true);

      // Initialize the tree
      gTree = document.getElementById("tree");
      gTreeBox = gTree.treeBoxObject;

      gView = new inTreeView();
      gView.mRowCount = 5;

      // Fire 'TreeViewChanged' event
      gTreeBox.view = gView;

      // Fire 'TreeRowCountChanged' changed
      ++gView.mRowCount;
      gTreeBox.rowCountChanged(0, 1);

      // Wait for events.
      window.setTimeout(CheckEvents, 1000);
    }

    function doTest2()
    {
      // Make sure accessibles for the tree is created because it makes
      // sure accessible events will be fired.
      var treeAcc = gAccService.getAccessibleFor(gTree);

      // Makes sure tree children accessibles are created otherwise they won't
      // be a couse of name changed events.
      var children = treeAcc.children;

      // Fire 'TreeInvalidated' event by InvalidateColumn()
      var firstCol = gTree.columns.getFirstColumn();
      for (var i = 0; i < gView.mRowCount; i++) {
        var key = String(i) + firstCol.id;
        gView.mData[key] = key + "_col";
      }

      gTreeBox.invalidateColumn(firstCol);
    }

    function doTest3()
    {
      // Fire 'TreeInvalidated' event by InvalidateRow()
      var colCount = gTree.columns.count;
      for (var i = 0; i < colCount; i++) {
        var key = "1" + gTree.columns.getColumnAt(i).id;
        gView.mData[key] = key + "_row";
      }

      gTreeBox.invalidateRow(1);
    }

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

  <hbox flex="1" style="overflow: auto;">
    <body xmlns="http://www.w3.org/1999/xhtml">
      <a target="_blank"
         href="https://bugzilla.mozilla.org/show_bug.cgi?id=368835"
         title="Fire TreeViewChanged/TreeRowCountChanged events.">
        Mozilla Bug 368835
      </a><br/>
      <a target="_blank"
         href="https://bugzilla.mozilla.org/show_bug.cgi?id=308564"
         title="No accessibility events when data in a tree row changes.">
        Mozilla Bug 308564
      </a>
      <p id="display"></p>
      <div id="content" style="display: none">
      </div>
      <pre id="test">
      </pre>
    </body>

    <tree id="tree" flex="1">
      <treecols>
        <treecol id="col" flex="1" primary="true" label="column"/>
        <treecol id="scol" flex="1" label="column 2"/>
      </treecols>
      <treechildren id="treechildren"/>
    </tree>
  </hbox>

</window>

