575576 - cache position in set and set size, r=davidb, marcoz, sr=neil

This commit is contained in:
Alexander Surkov 2010-07-03 12:11:35 +09:00
parent 7dc26b1584
commit db76e79301
7 changed files with 301 additions and 206 deletions

View File

@ -0,0 +1,169 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "AccGroupInfo.h"
AccGroupInfo::AccGroupInfo(nsAccessible* aItem, PRUint32 aRole) :
mPosInSet(0), mSetSize(0), mParent(nsnull)
{
nsAccessible* parent = aItem->GetParent();
if (!parent)
return;
PRInt32 indexInParent = aItem->GetIndexInParent();
PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(aItem);
// Compute position in set.
mPosInSet = 1;
for (PRInt32 idx = indexInParent - 1; idx >=0 ; idx--) {
nsAccessible* sibling = parent->GetChildAt(idx);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
// If sibling is not visible and hasn't the same base role.
if (BaseRole(siblingRole) != aRole ||
nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)
continue;
// Check if it's hierarchical flatten structure, i.e. if the sibling
// level is lesser than this one then group is ended, if the sibling level
// is greater than this one then the group is split by some child elements
// (group will be continued).
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level) {
mParent = sibling;
break;
}
// Skip subset.
if (siblingLevel > level)
continue;
// If the previous item in the group has calculated group information then
// build group information for this item based on found one.
if (sibling->mGroupInfo) {
mPosInSet += sibling->mGroupInfo->mPosInSet;
mParent = sibling->mGroupInfo->mParent;
mSetSize = sibling->mGroupInfo->mSetSize;
return;
}
mPosInSet++;
}
// Compute set size.
mSetSize = mPosInSet;
PRInt32 siblingCount = parent->GetChildCount();
for (PRInt32 idx = indexInParent + 1; idx < siblingCount; idx++) {
nsAccessible* sibling = parent->GetChildAt(idx);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
// If sibling is visible and has the same base role
if (BaseRole(siblingRole) != aRole ||
nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)
continue;
// and check if it's hierarchical flatten structure.
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
// Skip subset.
if (siblingLevel > level)
continue;
// If the next item in the group has calculated group information then
// build group information for this item based on found one.
if (sibling->mGroupInfo) {
mParent = sibling->mGroupInfo->mParent;
mSetSize = sibling->mGroupInfo->mSetSize;
return;
}
mSetSize++;
}
if (mParent)
return;
// Compute parent.
PRUint32 parentRole = nsAccUtils::Role(parent);
// In the case of ARIA row in treegrid, return treegrid since ARIA
// groups aren't used to organize levels in ARIA treegrids.
if (aRole == nsIAccessibleRole::ROLE_ROW &&
parentRole == nsIAccessibleRole::ROLE_TREE_TABLE) {
mParent = parent;
return;
}
// In the case of ARIA tree, a tree can be arranged by using ARIA groups
// to organize levels. In this case the parent of the tree item will be
// a group and the previous treeitem of that should be the tree item
// parent. Or, if the parent is something other than a tree we will
// return that.
if (parentRole != nsIAccessibleRole::ROLE_GROUPING) {
mParent = parent;
return;
}
nsAccessible* parentPrevSibling = parent->GetSiblingAtOffset(-1);
PRUint32 parentPrevSiblingRole = nsAccUtils::Role(parentPrevSibling);
if (parentPrevSiblingRole == nsIAccessibleRole::ROLE_TEXT_LEAF) {
// XXX Sometimes an empty text accessible is in the hierarchy here,
// although the text does not appear to be rendered, GetRenderedText()
// says that it is so we need to skip past it to find the true
// previous sibling.
parentPrevSibling = parentPrevSibling->GetSiblingAtOffset(-1);
parentPrevSiblingRole = nsAccUtils::Role(parentPrevSibling);
}
// Previous sibling of parent group is a tree item, this is the
// conceptual tree item parent.
if (parentPrevSiblingRole == nsIAccessibleRole::ROLE_OUTLINEITEM)
mParent = parentPrevSibling;
}

View File

@ -0,0 +1,96 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef AccGroupInfo_h_
#define AccGroupInfo_h_
#include "nsAccessible.h"
#include "nsAccUtils.h"
/**
* Calculate and store group information.
*/
class AccGroupInfo
{
public:
AccGroupInfo(nsAccessible* aItem, PRUint32 aRole);
~AccGroupInfo() { }
PRInt32 PosInSet() const { return mPosInSet; }
PRUint32 SetSize() const { return mSetSize; }
nsAccessible* GetConceptualParent() const { return mParent; }
/**
* Create group info.
*/
static AccGroupInfo* CreateGroupInfo(nsAccessible* aAccessible)
{
PRUint32 role = nsAccUtils::Role(aAccessible);
if (role != nsIAccessibleRole::ROLE_ROW &&
role != nsIAccessibleRole::ROLE_GRID_CELL &&
role != nsIAccessibleRole::ROLE_OUTLINEITEM &&
role != nsIAccessibleRole::ROLE_OPTION &&
role != nsIAccessibleRole::ROLE_LISTITEM &&
role != nsIAccessibleRole::ROLE_MENUITEM &&
role != nsIAccessibleRole::ROLE_CHECK_MENU_ITEM &&
role != nsIAccessibleRole::ROLE_RADIO_MENU_ITEM &&
role != nsIAccessibleRole::ROLE_RADIOBUTTON &&
role != nsIAccessibleRole::ROLE_PAGETAB)
return nsnull;
AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role));
return info;
}
private:
AccGroupInfo(const AccGroupInfo&);
AccGroupInfo& operator =(const AccGroupInfo&);
static PRUint32 BaseRole(PRUint32 aRole)
{
if (aRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
aRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
return nsIAccessibleRole::ROLE_MENUITEM;
return aRole;
}
PRUint32 mPosInSet;
PRUint32 mSetSize;
nsAccessible* mParent;
};
#endif

View File

@ -49,6 +49,7 @@ LIBXUL_LIBRARY = 1
CPPSRCS = \
AccCollector.cpp \
AccGroupInfo.cpp \
AccIterator.cpp \
filters.cpp \
nsAccDocManager.cpp \

View File

@ -367,106 +367,6 @@ nsAccUtils::GetAncestorWithRole(nsAccessible *aDescendant, PRUint32 aRole)
return nsnull;
}
void
nsAccUtils::GetARIATreeItemParent(nsIAccessible *aStartTreeItem,
nsIContent *aStartContent,
nsIAccessible **aTreeItemParentResult)
{
*aTreeItemParentResult = nsnull;
nsCOMPtr<nsIAccessible> parentAccessible;
aStartTreeItem->GetParent(getter_AddRefs(parentAccessible));
if (!parentAccessible)
return;
PRUint32 startTreeItemRole = nsAccUtils::Role(aStartTreeItem);
// Calculate tree grid row parent only if the row inside of ARIA treegrid.
if (startTreeItemRole == nsIAccessibleRole::ROLE_ROW) {
PRUint32 role = nsAccUtils::Role(parentAccessible);
if (role != nsIAccessibleRole::ROLE_TREE_TABLE)
return;
}
// This is a tree or treegrid that uses aria-level to define levels, so find
// the first previous sibling accessible where level is defined to be less
// than the current level.
nsAutoString levelStr;
if (nsAccUtils::HasDefinedARIAToken(aStartContent, nsAccessibilityAtoms::aria_level) &&
aStartContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_level, levelStr)) {
PRInt32 success;
PRInt32 level = levelStr.ToInteger(&success);
if (level > 1 && NS_SUCCEEDED(success)) {
nsCOMPtr<nsIAccessible> currentAccessible = aStartTreeItem, prevAccessible;
while (PR_TRUE) {
currentAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
currentAccessible.swap(prevAccessible);
nsCOMPtr<nsIAccessNode> accessNode = do_QueryInterface(currentAccessible);
if (!accessNode) {
break; // Reached top of tree, no higher level found
}
PRUint32 role = nsAccUtils::Role(currentAccessible);
if (role != startTreeItemRole)
continue;
nsCOMPtr<nsIDOMNode> treeItemNode;
accessNode->GetDOMNode(getter_AddRefs(treeItemNode));
nsCOMPtr<nsIContent> treeItemContent = do_QueryInterface(treeItemNode);
if (treeItemContent &&
nsAccUtils::HasDefinedARIAToken(treeItemContent,
nsAccessibilityAtoms::aria_level) &&
treeItemContent->GetAttr(kNameSpaceID_None,
nsAccessibilityAtoms::aria_level, levelStr)) {
if (levelStr.ToInteger(&success) < level && NS_SUCCEEDED(success)) {
NS_ADDREF(*aTreeItemParentResult = currentAccessible);
return;
}
}
}
}
}
// In the case of ARIA treegrid, return its parent since ARIA group isn't
// used to organize levels in ARIA treegrids.
if (startTreeItemRole == nsIAccessibleRole::ROLE_ROW) {
NS_ADDREF(*aTreeItemParentResult = parentAccessible);
return; // The container for the tree grid rows
}
// In the case of ARIA tree, a tree can be arranged by using role="group" to
// organize levels. In this case the parent of the tree item will be a group
// and the previous sibling of that should be the tree item parent. Or, if
// the parent is something other than a tree we will return that.
PRUint32 role = nsAccUtils::Role(parentAccessible);
if (role != nsIAccessibleRole::ROLE_GROUPING) {
NS_ADDREF(*aTreeItemParentResult = parentAccessible);
return; // The container for the tree items
}
nsCOMPtr<nsIAccessible> prevAccessible;
parentAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
if (!prevAccessible)
return;
role = nsAccUtils::Role(prevAccessible);
if (role == nsIAccessibleRole::ROLE_TEXT_LEAF) {
// XXX Sometimes an empty text accessible is in the hierarchy here,
// although the text does not appear to be rendered, GetRenderedText() says that it is
// so we need to skip past it to find the true previous sibling
nsCOMPtr<nsIAccessible> tempAccessible = prevAccessible;
tempAccessible->GetPreviousSibling(getter_AddRefs(prevAccessible));
if (!prevAccessible)
return;
role = nsAccUtils::Role(prevAccessible);
}
if (role == nsIAccessibleRole::ROLE_OUTLINEITEM) {
// Previous sibling of parent group is a tree item -- this is the conceptual tree item parent
NS_ADDREF(*aTreeItemParentResult = prevAccessible);
}
}
nsAccessible *
nsAccUtils::GetSelectableContainer(nsAccessible *aAccessible, PRUint32 aState)
{

View File

@ -198,19 +198,6 @@ public:
static nsAccessible * GetAncestorWithRole(nsAccessible *aDescendant,
PRUint32 aRole);
/**
* For an ARIA tree item , get the accessible that represents its conceptual parent.
* This method will use the correct method for the given way the tree is constructed.
* The conceptual parent is what the user sees as the parent, not the DOM or accessible parent.
* @param aStartTreeItem The tree item to get the parent for
* @param aStartTreeItemContent The content node for the tree item
* @param The tree item's parent, or null if none
*/
static void
GetARIATreeItemParent(nsIAccessible *aStartTreeItem,
nsIContent *aStartTreeItemContent,
nsIAccessible **aTreeItemParent);
/**
* Return single or multi selectable container for the given item.
*

View File

@ -41,6 +41,7 @@
#include "nsIXBLAccessible.h"
#include "AccGroupInfo.h"
#include "AccIterator.h"
#include "nsAccUtils.h"
#include "nsARIAMap.h"
@ -2125,11 +2126,12 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType,
(mRoleMapEntry->role == nsIAccessibleRole::ROLE_OUTLINEITEM ||
mRoleMapEntry->role == nsIAccessibleRole::ROLE_ROW)) {
nsCOMPtr<nsIAccessible> accTarget;
nsAccUtils::GetARIATreeItemParent(this, mContent,
getter_AddRefs(accTarget));
AccGroupInfo* groupInfo = GetGroupInfo();
if (!groupInfo)
return NS_OK_NO_RELATION_TARGET;
return nsRelUtils::AddTarget(aRelationType, aRelation, accTarget);
return nsRelUtils::AddTarget(aRelationType, aRelation,
groupInfo->GetConceptualParent());
}
// If accessible is in its own Window, or is the root of a document,
@ -3091,98 +3093,24 @@ nsAccessible::GetActionRule(PRUint32 aStates)
return eNoAction;
}
AccGroupInfo*
nsAccessible::GetGroupInfo()
{
if (mGroupInfo)
return mGroupInfo;
mGroupInfo = AccGroupInfo::CreateGroupInfo(this);
return mGroupInfo;
}
void
nsAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet, PRInt32 *aSetSize)
{
PRUint32 role = nsAccUtils::Role(this);
if (role != nsIAccessibleRole::ROLE_LISTITEM &&
role != nsIAccessibleRole::ROLE_MENUITEM &&
role != nsIAccessibleRole::ROLE_CHECK_MENU_ITEM &&
role != nsIAccessibleRole::ROLE_RADIO_MENU_ITEM &&
role != nsIAccessibleRole::ROLE_RADIOBUTTON &&
role != nsIAccessibleRole::ROLE_PAGETAB &&
role != nsIAccessibleRole::ROLE_OPTION &&
role != nsIAccessibleRole::ROLE_OUTLINEITEM &&
role != nsIAccessibleRole::ROLE_ROW &&
role != nsIAccessibleRole::ROLE_GRID_CELL)
return;
PRUint32 baseRole = role;
if (role == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
role == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
baseRole = nsIAccessibleRole::ROLE_MENUITEM;
nsAccessible* parent = GetParent();
NS_ENSURE_TRUE(parent,);
PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(this);
// Compute 'posinset'.
PRInt32 positionInGroup = 1;
for (PRInt32 idx = mIndexInParent - 1; idx >= 0; idx--) {
nsAccessible* sibling = parent->GetChildAt(idx);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
PRUint32 siblingBaseRole = siblingRole;
if (siblingRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
siblingRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
siblingBaseRole = nsIAccessibleRole::ROLE_MENUITEM;
// If sibling is visible and has the same base role
if (siblingBaseRole == baseRole &&
!(nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
// and check if it's hierarchical flatten structure, i.e. if the sibling
// level is lesser than this one then group is ended, if the sibling level
// is greater than this one then the group is splited by some child
// elements (group will be continued).
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
else if (level == siblingLevel)
++ positionInGroup;
}
AccGroupInfo* groupInfo = GetGroupInfo();
if (groupInfo) {
*aPosInSet = groupInfo->PosInSet();
*aSetSize = groupInfo->SetSize();
}
// Compute 'setsize'.
PRInt32 setSize = positionInGroup;
PRInt32 siblingCount = parent->GetChildCount();
for (PRInt32 idx = mIndexInParent + 1; idx < siblingCount; idx++) {
nsAccessible* sibling = parent->GetChildAt(idx);
NS_ENSURE_TRUE(sibling,);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
PRUint32 siblingBaseRole = siblingRole;
if (siblingRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
siblingRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
siblingBaseRole = nsIAccessibleRole::ROLE_MENUITEM;
// If sibling is visible and has the same base role
if (siblingBaseRole == baseRole &&
!(nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
// and check if it's hierarchical flatten structure.
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
else if (level == siblingLevel)
++ setSize;
}
}
*aPosInSet = positionInGroup;
*aSetSize = setSize;
}
PRInt32

View File

@ -52,6 +52,7 @@
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"
class AccGroupInfo;
class nsAccessible;
class nsAccEvent;
struct nsRoleMapEntry;
@ -324,7 +325,12 @@ protected:
* Set accessible parent and index in parent.
*/
void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
void UnbindFromParent() { mParent = nsnull; mIndexInParent = -1; }
void UnbindFromParent()
{
mParent = nsnull;
mIndexInParent = -1;
mGroupInfo = nsnull;
}
/**
* Return sibling accessible at the given offset.
@ -424,6 +430,11 @@ protected:
*/
PRUint32 GetActionRule(PRUint32 aStates);
/**
* Return group info.
*/
AccGroupInfo* GetGroupInfo();
/**
* Fires platform accessible event. It's notification method only. It does
* change nothing on Gecko side. Don't use it until you're sure what you do
@ -440,6 +451,9 @@ protected:
PRBool mAreChildrenInitialized;
PRInt32 mIndexInParent;
nsAutoPtr<AccGroupInfo> mGroupInfo;
friend class AccGroupInfo;
nsRoleMapEntry *mRoleMapEntry; // Non-null indicates author-supplied role; possibly state & value as well
};