nsAccessible*
nsXULTreeAccessible::GetTreeItemAccessible(PRInt32 aRow)
{
  if (aRow < 0 || IsDefunct())
    return nsnull;

  PRInt32 rowCount = 0;
  nsresult rv = mTreeView->GetRowCount(&rowCount);
  if (NS_FAILED(rv) || aRow >= rowCount)
    return nsnull;

  void *key = reinterpret_cast<void*>(aRow);
  nsAccessible* cachedTreeItem = mAccessibleCache.GetWeak(key);
  if (cachedTreeItem)
    return cachedTreeItem;

  nsRefPtr<nsAccessible> treeItem = CreateTreeItemAccessible(aRow);
  if (treeItem) {
    if (mAccessibleCache.Put(key, treeItem)) {
      if (GetDocAccessible()->BindToDocument(treeItem, nsnull))
        return treeItem;

      mAccessibleCache.Remove(key);
    }
  }

  return nsnull;
}
void 
nsHTMLImageMapAccessible::CacheChildren()
{
  if (!mMapElement)
    return;

  nsCOMPtr<nsIDOMHTMLCollection> mapAreas;
  mMapElement->GetAreas(getter_AddRefs(mapAreas));
  if (!mapAreas)
    return;

  nsDocAccessible* document = GetDocAccessible();

  PRUint32 areaCount = 0;
  mapAreas->GetLength(&areaCount);

  for (PRUint32 areaIdx = 0; areaIdx < areaCount; areaIdx++) {
    nsCOMPtr<nsIDOMNode> areaNode;
    mapAreas->Item(areaIdx, getter_AddRefs(areaNode));
    if (!areaNode)
      return;

    nsCOMPtr<nsIContent> areaContent(do_QueryInterface(areaNode));
    nsRefPtr<nsAccessible> area =
      new nsHTMLAreaAccessible(areaContent, mWeakShell);

    if (!document->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent)) ||
        !AppendChild(area)) {
      return;
    }
  }
}
Esempio n. 3
0
NS_IMETHODIMP
nsAccessNode::GetDocument(nsIAccessibleDocument **aDocument)
{
  NS_ENSURE_ARG_POINTER(aDocument);

  NS_IF_ADDREF(*aDocument = GetDocAccessible());
  return NS_OK;
}
void
nsXULButtonAccessible::CacheChildren()
{
  // In general XUL button has not accessible children. Nevertheless menu
  // buttons can have button (@type="menu-button") and popup accessibles
  // (@type="menu-button" or @type="menu").

  // XXX: no children until the button is menu button. Probably it's not
  // totally correct but in general AT wants to have leaf buttons.
  PRBool isMenu = mContent->AttrValueIs(kNameSpaceID_None,
                                       nsAccessibilityAtoms::type,
                                       nsAccessibilityAtoms::menu,
                                       eCaseMatters);

  PRBool isMenuButton = isMenu ?
    PR_FALSE :
    mContent->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
                          nsAccessibilityAtoms::menuButton, eCaseMatters);

  if (!isMenu && !isMenuButton)
    return;

  nsAccessible* menupopup = nsnull;
  nsAccessible* button = nsnull;

  nsAccTreeWalker walker(mWeakShell, mContent, PR_TRUE);

  nsAccessible* child = nsnull;
  while ((child = walker.NextChild())) {
    PRUint32 role = child->Role();

    if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
      // Get an accessible for menupopup or panel elements.
      menupopup = child;

    } else if (isMenuButton && role == nsIAccessibleRole::ROLE_PUSHBUTTON) {
      // Button type="menu-button" contains a real button. Get an accessible
      // for it. Ignore dropmarker button which is placed as a last child.
      button = child;
      break;

    } else {
      // Unbind rejected accessible from document.
      GetDocAccessible()->UnbindFromDocument(child);
    }
  }

  if (!menupopup)
    return;

  AppendChild(menupopup);
  if (button)
    AppendChild(button);
}
nsHTMLLIAccessible::
  nsHTMLLIAccessible(nsIContent* aContent, nsIWeakReference* aShell) :
  nsHyperTextAccessibleWrap(aContent, aShell), mBullet(nsnull)
{
  mFlags |= eHTMLListItemAccessible;

  nsBlockFrame* blockFrame = do_QueryFrame(GetFrame());
  if (blockFrame && blockFrame->HasBullet()) {
    mBullet = new nsHTMLListBulletAccessible(mContent, mWeakShell);
    if (!GetDocAccessible()->BindToDocument(mBullet, nsnull))
      mBullet = nsnull;
  }
}
void
nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
{
  if (IsDefunct())
    return;

  // Do not invalidate the cache if rows have been inserted.
  if (aCount > 0)
    return;

  nsDocAccessible* document = GetDocAccessible();

  // Fire destroy event for removed tree items and delete them from caches.
  for (PRInt32 rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) {

    void* key = reinterpret_cast<void*>(rowIdx);
    nsAccessible* treeItem = mAccessibleCache.GetWeak(key);

    if (treeItem) {
      nsRefPtr<AccEvent> event =
        new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem);
      nsEventShell::FireEvent(event);

      // Unbind from document, shutdown and remove from tree cache.
      document->UnbindFromDocument(treeItem);
      mAccessibleCache.Remove(key);
    }
  }

  // We dealt with removed tree items already however we may keep tree items
  // having row indexes greater than row count. We should remove these dead tree
  // items silently from caches.
  PRInt32 newRowCount = 0;
  nsresult rv = mTreeView->GetRowCount(&newRowCount);
  if (NS_FAILED(rv))
    return;

  PRInt32 oldRowCount = newRowCount - aCount;

  for (PRInt32 rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {

    void *key = reinterpret_cast<void*>(rowIdx);
    nsAccessible* treeItem = mAccessibleCache.GetWeak(key);

    if (treeItem) {
      // Unbind from document, shutdown and remove from tree cache.
      document->UnbindFromDocument(treeItem);
      mAccessibleCache.Remove(key);
    }
  }
}
nsresult
nsHTMLTextAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
{
  nsresult rv = nsTextAccessible::GetStateInternal(aState, aExtraState);
  NS_ENSURE_A11Y_SUCCESS(rv, rv);

  nsDocAccessible *docAccessible = GetDocAccessible();
  if (docAccessible) {
     PRUint32 state, extState;
     docAccessible->GetState(&state, &extState);
     if (0 == (extState & nsIAccessibleStates::EXT_STATE_EDITABLE)) {
       *aState |= nsIAccessibleStates::STATE_READONLY; // Links not focusable in editor
     }
  }

  return NS_OK;
}
void
nsXULTreeAccessible::TreeViewChanged()
{
  if (IsDefunct())
    return;

  // Fire reorder event on tree accessible on accessible tree (do not fire
  // show/hide events on tree items because it can be expensive to fire them for
  // each tree item.
  nsRefPtr<AccEvent> reorderEvent =
    new AccEvent(nsIAccessibleEvent::EVENT_REORDER, this, eAutoDetect,
                 AccEvent::eCoalesceFromSameSubtree);
  if (reorderEvent)
    GetDocAccessible()->FireDelayedAccessibleEvent(reorderEvent);

  // Clear cache.
  ClearCache(mAccessibleCache);
  mTree->GetView(getter_AddRefs(mTreeView));
}
void
nsHTMLLIAccessible::UpdateBullet(bool aHasBullet)
{
  if (aHasBullet == !!mBullet) {
    NS_NOTREACHED("Bullet and accessible are in sync already!");
    return;
  }

  nsDocAccessible* document = GetDocAccessible();
  if (aHasBullet) {
    mBullet = new nsHTMLListBulletAccessible(mContent, mWeakShell);
    if (document->BindToDocument(mBullet, nsnull)) {
      InsertChildAt(0, mBullet);
    }
  } else {
    RemoveChild(mBullet);
    document->UnbindFromDocument(mBullet);
    mBullet = nsnull;
  }

  // XXXtodo: fire show/hide and reorder events. That's hard to make it
  // right now because coalescence happens by DOM node.
}
Esempio n. 10
0
NS_IMETHODIMP
nsHTMLTableAccessible::IsProbablyForLayout(PRBool *aIsProbablyForLayout)
{
  // Implement a heuristic to determine if table is most likely used for layout
  // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells
  // at the beginning or end of a row/col, and especially when they occur at the edge of a table?
  // XXX expose this info via object attributes to AT-SPI

  // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC
  // This will allow release trunk builds to be used by testers to refine the algorithm
  // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release
#ifdef SHOW_LAYOUT_HEURISTIC
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
  { *aIsProbablyForLayout = isLayout; \
    mLayoutHeuristic = isLayout ? NS_LITERAL_STRING("layout table: ") : NS_LITERAL_STRING("data table: "); \
    mLayoutHeuristic += NS_LITERAL_STRING(heuristic); return NS_OK; }
#else
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { *aIsProbablyForLayout = isLayout; return NS_OK; }
#endif

  *aIsProbablyForLayout = PR_FALSE;

  if (IsDefunct())
    return NS_ERROR_FAILURE;

  nsDocAccessible *docAccessible = GetDocAccessible();
  if (docAccessible) {
    PRUint32 state, extState;
    docAccessible->GetState(&state, &extState);
    if (extState & nsIAccessibleStates::EXT_STATE_EDITABLE) {  // Need to see all elements while document is being edited
      RETURN_LAYOUT_ANSWER(PR_FALSE, "In editable document");
    }
  }

  // Check to see if an ARIA role overrides the role from native markup,
  // but for which we still expose table semantics (treegrid, for example).
  PRBool hasNonTableRole =
    (nsAccUtils::Role(this) != nsIAccessibleRole::ROLE_TABLE);
  if (hasNonTableRole) {
    RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute");
  }

  if (mContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::role)) {
    // Role attribute is present, but overridden roles have already been dealt with.
    // Only landmarks and other roles that don't override the role from native
    // markup are left to deal with here.
    RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute, weak role, and role is table");
  }
  
  // Check for legitimate data table elements or attributes
  nsAutoString summary;
  if ((mContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::summary, summary) &&
       !summary.IsEmpty()) || 
      HasDescendant(NS_LITERAL_STRING("caption"), PR_FALSE) ||
      HasDescendant(NS_LITERAL_STRING("th")) ||
      HasDescendant(NS_LITERAL_STRING("thead")) ||
      HasDescendant(NS_LITERAL_STRING("tfoot")) ||
      HasDescendant(NS_LITERAL_STRING("colgroup"))) {
    RETURN_LAYOUT_ANSWER(PR_FALSE, "Has caption, summary, th, thead, tfoot or colgroup -- legitimate table structures");
  }
  if (HasDescendant(NS_LITERAL_STRING("table"))) {
    RETURN_LAYOUT_ANSWER(PR_TRUE, "Has a nested table within it");
  }
  
  // If only 1 column or only 1 row, it's for layout
  PRInt32 columns, rows;
  GetColumnCount(&columns);
  if (columns <=1) {
    RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 column");
  }
  GetRowCount(&rows);
  if (rows <=1) {
    RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 row");
  }

  // Check for many columns
  if (columns >= 5) {
    RETURN_LAYOUT_ANSWER(PR_FALSE, ">=5 columns");
  }
  
  // Now we know there are 2-4 columns and 2 or more rows
  // Check to see if there are visible borders on the cells
  // XXX currently, we just check the first cell -- do we really need to do more?
  nsCOMPtr<nsIDOMElement> cellElement;
  nsresult rv = GetCellAt(0, 0, *getter_AddRefs(cellElement));
  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);

  nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
  NS_ENSURE_TRUE(cellContent, NS_ERROR_FAILURE);
  nsIFrame *cellFrame = cellContent->GetPrimaryFrame();
  if (!cellFrame) {
    return NS_OK;
  }
  nsMargin border;
  cellFrame->GetBorder(border);
  if (border.top && border.bottom && border.left && border.right) {
    RETURN_LAYOUT_ANSWER(PR_FALSE, "Has nonzero border-width on table cell");
  }

  /**
   * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
   */

  // Check for styled background color across the row
  // Alternating background color is a common way 
  nsCOMPtr<nsIDOMNodeList> nodeList;
  nsCOMPtr<nsIDOMElement> tableElt(do_QueryInterface(mContent));    
  tableElt->GetElementsByTagName(NS_LITERAL_STRING("tr"), getter_AddRefs(nodeList));
  NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
  PRUint32 length;
  nodeList->GetLength(&length);
  nsAutoString color, lastRowColor;
  for (PRUint32 rowCount = 0; rowCount < length; rowCount ++) {
    nsCOMPtr<nsIDOMNode> rowNode;
    nodeList->Item(rowCount, getter_AddRefs(rowNode));
    nsCOMPtr<nsIContent> rowContent(do_QueryInterface(rowNode));

    nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl =
      nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), rowContent);
    NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE);

    lastRowColor = color;
    styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), color);
    if (rowCount > 0 && PR_FALSE == lastRowColor.Equals(color)) {
      RETURN_LAYOUT_ANSWER(PR_FALSE, "2 styles of row background color, non-bordered");
    }
  }

  // Check for many rows
  const PRInt32 kMaxLayoutRows = 20;
  if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data
    RETURN_LAYOUT_ANSWER(PR_FALSE, ">= kMaxLayoutRows (20) and non-bordered");
  }

  // Check for very wide table
  nsAutoString styledWidth;
  GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("width"), styledWidth);
  if (styledWidth.EqualsLiteral("100%")) {
    RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns and 100% width");
  }
  if (styledWidth.Find(NS_LITERAL_STRING("px"))) { // Hardcoded in pixels
    nsIFrame *tableFrame = GetFrame();
    NS_ENSURE_TRUE(tableFrame , NS_ERROR_FAILURE);
    nsSize tableSize  = tableFrame->GetSize();

    nsDocAccessible *docAccessible = GetDocAccessible();
    NS_ENSURE_TRUE(docAccessible, NS_ERROR_FAILURE);
    nsIFrame *docFrame = docAccessible->GetFrame();
    NS_ENSURE_TRUE(docFrame , NS_ERROR_FAILURE);

    nsSize docSize = docFrame->GetSize();
    if (docSize.width > 0) {
      PRInt32 percentageOfDocWidth = (100 * tableSize.width) / docSize.width;
      if (percentageOfDocWidth > 95) {
        // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
        // Probably for layout
        RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns, width hardcoded in pixels and 95% of document width");
      }
    }
  }

  // Two column rules
  if (rows * columns <= 10) {
    RETURN_LAYOUT_ANSWER(PR_TRUE, "2-4 columns, 10 cells or less, non-bordered");
  }

  if (HasDescendant(NS_LITERAL_STRING("embed")) ||
      HasDescendant(NS_LITERAL_STRING("object")) ||
      HasDescendant(NS_LITERAL_STRING("applet")) ||
      HasDescendant(NS_LITERAL_STRING("iframe"))) {
    RETURN_LAYOUT_ANSWER(PR_TRUE, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
  }

  RETURN_LAYOUT_ANSWER(PR_FALSE, "no layout factor strong enough, so will guess data");
}