NS_IMETHODIMP
nsARIAGridAccessible::SelectColumn(PRInt32 aColumn)
{
  NS_ENSURE_ARG(IsValidColumn(aColumn));

  if (IsDefunct())
    return NS_ERROR_FAILURE;

  AccIterator rowIter(this, filters::GetRow);

  nsAccessible *row = nsnull;
  while ((row = rowIter.GetNext())) {
    // Unselect all cells in the row.
    nsresult rv = SetARIASelected(row, PR_FALSE);
    NS_ENSURE_SUCCESS(rv, rv);

    // Select cell at the column index.
    nsAccessible *cell = GetCellInRowAt(row, aColumn);
    if (cell) {
      rv = SetARIASelected(cell, PR_TRUE);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  return NS_OK;
}
NS_IMETHODIMP
nsARIAGridAccessible::UnselectRow(PRInt32 aRow)
{
  if (IsDefunct())
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsIAccessible> row = GetRowAt(aRow);
  NS_ENSURE_ARG(row);

  return SetARIASelected(row, PR_FALSE);
}
NS_IMETHODIMP
nsARIAGridAccessible::SelectColumn(PRInt32 aColumn)
{
  NS_ENSURE_ARG(IsValidColumn(aColumn));

  if (IsDefunct())
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsIAccessible> row;
  while ((row = GetNextRow(row))) {
    // Unselect all cells in the row.
    nsresult rv = SetARIASelected(row, PR_FALSE);
    NS_ENSURE_SUCCESS(rv, rv);

    // Select cell at the column index.
    nsCOMPtr<nsIAccessible> cell = GetCellInRowAt(row, aColumn);
    if (cell) {
      rv = SetARIASelected(cell, PR_TRUE);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  return NS_OK;
}
NS_IMETHODIMP
nsARIAGridAccessible::SelectRow(PRInt32 aRow)
{
  NS_ENSURE_ARG(IsValidRow(aRow));

  if (IsDefunct())
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsIAccessible> row;
  for (PRInt32 rowIdx = 0; row = GetNextRow(row); rowIdx++) {
    nsresult rv = SetARIASelected(row, rowIdx == aRow);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  return NS_OK;
}
NS_IMETHODIMP
nsARIAGridAccessible::SelectRow(PRInt32 aRow)
{
  NS_ENSURE_ARG(IsValidRow(aRow));

  if (IsDefunct())
    return NS_ERROR_FAILURE;

  AccIterator rowIter(this, filters::GetRow);

  nsAccessible *row = nsnull;
  for (PRInt32 rowIdx = 0; (row = rowIter.GetNext()); rowIdx++) {
    nsresult rv = SetARIASelected(row, rowIdx == aRow);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  return NS_OK;
}
nsresult
nsARIAGridAccessible::SetARIASelected(nsIAccessible *aAccessible,
                                      PRBool aIsSelected, PRBool aNotify)
{
  nsRefPtr<nsAccessible> acc = nsAccUtils::QueryAccessible(aAccessible);
  nsCOMPtr<nsIDOMNode> node;
  acc->GetDOMNode(getter_AddRefs(node));
  NS_ENSURE_STATE(node);

  nsCOMPtr<nsIContent> content(do_QueryInterface(node));

  nsresult rv = NS_OK;
  if (aIsSelected)
    rv = content->SetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_selected,
                          NS_LITERAL_STRING("true"), aNotify);
  else
    rv = content->UnsetAttr(kNameSpaceID_None,
                            nsAccessibilityAtoms::aria_selected, aNotify);

  NS_ENSURE_SUCCESS(rv, rv);

  // No "smart" select/unselect for internal call.
  if (!aNotify)
    return NS_OK;

  // If row or cell accessible was selected then we're able to not bother about
  // selection of its cells or its row because our algorithm is row oriented,
  // i.e. we check selection on row firstly and then on cells.
  if (aIsSelected)
    return NS_OK;

  PRUint32 role = nsAccUtils::Role(aAccessible);

  // If the given accessible is row that was unselected then remove
  // aria-selected from cell accessible.
  if (role == nsIAccessibleRole::ROLE_ROW) {
    nsCOMPtr<nsIAccessible> cell;
    while ((cell = GetNextCellInRow(aAccessible, cell))) {
      rv = SetARIASelected(cell, PR_FALSE, PR_FALSE);
      NS_ENSURE_SUCCESS(rv, rv);
    }
    return NS_OK;
  }

  // If the given accessible is cell that was unselected and its row is selected
  // then remove aria-selected from row and put aria-selected on
  // siblings cells.
  if (role == nsIAccessibleRole::ROLE_GRID_CELL ||
      role == nsIAccessibleRole::ROLE_ROWHEADER ||
      role == nsIAccessibleRole::ROLE_COLUMNHEADER) {
    nsCOMPtr<nsIAccessible> row;
    aAccessible->GetParent(getter_AddRefs(row));

    if (nsAccUtils::Role(row) == nsIAccessibleRole::ROLE_ROW &&
        nsAccUtils::IsARIASelected(row)) {
      rv = SetARIASelected(row, PR_FALSE, PR_FALSE);
      NS_ENSURE_SUCCESS(rv, rv);

      nsCOMPtr<nsIAccessible> cell;
      while ((cell = GetNextCellInRow(row, cell))) {
        if (cell != aAccessible) {
          rv = SetARIASelected(cell, PR_TRUE, PR_FALSE);
          NS_ENSURE_SUCCESS(rv, rv);
        }
      }
    }
  }

  return NS_OK;
}
nsresult
nsARIAGridAccessible::SetARIASelected(nsAccessible *aAccessible,
                                      PRBool aIsSelected, PRBool aNotify)
{
  nsIContent *content = aAccessible->GetContent();
  NS_ENSURE_STATE(content);

  nsresult rv = NS_OK;
  if (aIsSelected)
    rv = content->SetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_selected,
                          NS_LITERAL_STRING("true"), aNotify);
  else
    rv = content->UnsetAttr(kNameSpaceID_None,
                            nsAccessibilityAtoms::aria_selected, aNotify);

  NS_ENSURE_SUCCESS(rv, rv);

  // No "smart" select/unselect for internal call.
  if (!aNotify)
    return NS_OK;

  // If row or cell accessible was selected then we're able to not bother about
  // selection of its cells or its row because our algorithm is row oriented,
  // i.e. we check selection on row firstly and then on cells.
  if (aIsSelected)
    return NS_OK;

  PRUint32 role = aAccessible->Role();

  // If the given accessible is row that was unselected then remove
  // aria-selected from cell accessible.
  if (role == nsIAccessibleRole::ROLE_ROW) {
    AccIterator cellIter(aAccessible, filters::GetCell);
    nsAccessible *cell = nsnull;

    while ((cell = cellIter.GetNext())) {
      rv = SetARIASelected(cell, PR_FALSE, PR_FALSE);
      NS_ENSURE_SUCCESS(rv, rv);
    }
    return NS_OK;
  }

  // If the given accessible is cell that was unselected and its row is selected
  // then remove aria-selected from row and put aria-selected on
  // siblings cells.
  if (role == nsIAccessibleRole::ROLE_GRID_CELL ||
      role == nsIAccessibleRole::ROLE_ROWHEADER ||
      role == nsIAccessibleRole::ROLE_COLUMNHEADER) {
    nsAccessible *row = aAccessible->GetParent();

    if (row && row->Role() == nsIAccessibleRole::ROLE_ROW &&
        nsAccUtils::IsARIASelected(row)) {
      rv = SetARIASelected(row, PR_FALSE, PR_FALSE);
      NS_ENSURE_SUCCESS(rv, rv);

      AccIterator cellIter(row, filters::GetCell);
      nsAccessible *cell = nsnull;
      while ((cell = cellIter.GetNext())) {
        if (cell != aAccessible) {
          rv = SetARIASelected(cell, PR_TRUE, PR_FALSE);
          NS_ENSURE_SUCCESS(rv, rv);
        }
      }
    }
  }

  return NS_OK;
}