Bug 576777 - provide quick access to link index, r=marcoz, davidb, sr=neil

This commit is contained in:
Alexander Surkov 2010-08-15 20:28:49 +09:00
parent 47126da4d8
commit 8d575c4458
9 changed files with 262 additions and 118 deletions

View File

@ -92,7 +92,7 @@ AccCollector::EnsureNGetObject(PRUint32 aIndex)
if (!mFilterFunc(child))
continue;
mObjects.AppendElement(child);
AppendObject(child);
if (mObjects.Length() - 1 == aIndex)
return mObjects[aIndex];
}
@ -109,10 +109,39 @@ AccCollector::EnsureNGetIndex(nsAccessible* aAccessible)
if (!mFilterFunc(child))
continue;
mObjects.AppendElement(child);
AppendObject(child);
if (child == aAccessible)
return mObjects.Length() - 1;
}
return -1;
}
void
AccCollector::AppendObject(nsAccessible* aAccessible)
{
mObjects.AppendElement(aAccessible);
}
////////////////////////////////////////////////////////////////////////////////
// EmbeddedObjCollector
////////////////////////////////////////////////////////////////////////////////
PRInt32
EmbeddedObjCollector::GetIndexAt(nsAccessible *aAccessible)
{
if (aAccessible->mParent != mRoot)
return -1;
if (aAccessible->mIndexOfEmbeddedChild != -1)
return aAccessible->mIndexOfEmbeddedChild;
return mFilterFunc(aAccessible) ? EnsureNGetIndex(aAccessible) : -1;
}
void
EmbeddedObjCollector::AppendObject(nsAccessible* aAccessible)
{
aAccessible->mIndexOfEmbeddedChild = mObjects.Length();
mObjects.AppendElement(aAccessible);
}

View File

@ -66,7 +66,7 @@ public:
/**
* Return index of the given accessible within the collection.
*/
PRInt32 GetIndexAt(nsAccessible* aAccessible);
virtual PRInt32 GetIndexAt(nsAccessible* aAccessible);
protected:
/**
@ -79,6 +79,11 @@ protected:
*/
PRInt32 EnsureNGetIndex(nsAccessible* aAccessible);
/**
* Append the object to collection.
*/
virtual void AppendObject(nsAccessible* aAccessible);
filters::FilterFuncPtr mFilterFunc;
nsAccessible* mRoot;
PRInt32 mRootChildIdx;
@ -91,4 +96,26 @@ private:
AccCollector& operator =(const AccCollector&);
};
/**
* Collect embedded objects. Provide quick access to accessible by index and
* vice versa.
*/
class EmbeddedObjCollector : public AccCollector
{
public:
virtual ~EmbeddedObjCollector() { };
public:
virtual PRInt32 GetIndexAt(nsAccessible* aAccessible);
protected:
// Make sure it's used by nsAccessible class only.
EmbeddedObjCollector(nsAccessible* aRoot) :
AccCollector(aRoot, filters::GetEmbeddedObject) { }
virtual void AppendObject(nsAccessible* aAccessible);
friend class nsAccessible;
};
#endif

View File

@ -193,8 +193,8 @@ nsresult nsAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
nsAccessible::nsAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
nsAccessNodeWrap(aContent, aShell),
mParent(nsnull), mAreChildrenInitialized(PR_FALSE), mIndexInParent(-1),
mRoleMapEntry(nsnull)
mParent(nsnull), mIndexInParent(-1), mChildrenFlags(eChildrenUninitialized),
mIndexOfEmbeddedChild(-1), mRoleMapEntry(nsnull)
{
#ifdef NS_DEBUG_X
{
@ -2751,6 +2751,7 @@ nsAccessible::UnbindFromParent()
{
mParent = nsnull;
mIndexInParent = -1;
mIndexOfEmbeddedChild = -1;
mGroupInfo = nsnull;
}
@ -2763,8 +2764,9 @@ nsAccessible::InvalidateChildren()
child->UnbindFromParent();
}
mEmbeddedObjCollector = nsnull;
mChildren.Clear();
mAreChildrenInitialized = PR_FALSE;
mChildrenFlags = eChildrenUninitialized;
}
PRBool
@ -2773,6 +2775,9 @@ nsAccessible::AppendChild(nsAccessible* aChild)
if (!mChildren.AppendElement(aChild))
return PR_FALSE;
if (nsAccUtils::IsText(aChild))
mChildrenFlags = eMixedChildren;
aChild->BindToParent(this, mChildren.Length() - 1);
return PR_TRUE;
}
@ -2786,6 +2791,11 @@ nsAccessible::InsertChildAt(PRUint32 aIndex, nsAccessible* aChild)
for (PRUint32 idx = aIndex + 1; idx < mChildren.Length(); idx++)
mChildren[idx]->mIndexInParent++;
if (nsAccUtils::IsText(aChild))
mChildrenFlags = eMixedChildren;
mEmbeddedObjCollector = nsnull;
aChild->BindToParent(this, aIndex);
return PR_TRUE;
}
@ -2800,6 +2810,8 @@ nsAccessible::RemoveChild(nsAccessible* aChild)
mChildren[idx]->mIndexInParent--;
mChildren.RemoveElementAt(aChild->mIndexInParent);
mEmbeddedObjCollector = nsnull;
aChild->UnbindFromParent();
return PR_TRUE;
}
@ -2877,6 +2889,53 @@ nsAccessible::GetIndexInParent()
return mIndexInParent;
}
PRInt32
nsAccessible::GetEmbeddedChildCount()
{
if (EnsureChildren())
return -1;
if (mChildrenFlags == eMixedChildren) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ? mEmbeddedObjCollector->Count() : -1;
}
return GetChildCount();
}
nsAccessible*
nsAccessible::GetEmbeddedChildAt(PRUint32 aIndex)
{
if (EnsureChildren())
return nsnull;
if (mChildrenFlags == eMixedChildren) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
mEmbeddedObjCollector->GetAccessibleAt(aIndex) : nsnull;
}
return GetChildAt(aIndex);
}
PRInt32
nsAccessible::GetIndexOfEmbeddedChild(nsAccessible* aChild)
{
if (EnsureChildren())
return -1;
if (mChildrenFlags == eMixedChildren) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
mEmbeddedObjCollector->GetIndexAt(aChild) : -1;
}
return GetIndexOf(aChild);
}
#ifdef DEBUG
PRBool
nsAccessible::IsInCache()
@ -2912,7 +2971,8 @@ nsAccessible::TestChildCache(nsAccessible *aCachedChild)
#ifdef DEBUG
PRInt32 childCount = mChildren.Length();
if (childCount == 0) {
NS_ASSERTION(!mAreChildrenInitialized, "No children but initialized!");
NS_ASSERTION(mChildrenFlags == eChildrenUninitialized,
"No children but initialized!");
return;
}
@ -2933,14 +2993,15 @@ PRBool
nsAccessible::EnsureChildren()
{
if (IsDefunct()) {
mAreChildrenInitialized = PR_FALSE;
mChildrenFlags = eChildrenUninitialized;
return PR_TRUE;
}
if (mAreChildrenInitialized)
if (mChildrenFlags != eChildrenUninitialized)
return PR_FALSE;
mAreChildrenInitialized = PR_TRUE; // Prevent reentry
// State is embedded children until text leaf accessible is appended.
mChildrenFlags = eEmbeddedChildren; // Prevent reentry
CacheChildren();
return PR_FALSE;

View File

@ -53,6 +53,7 @@
#include "nsRefPtrHashtable.h"
class AccGroupInfo;
class EmbeddedObjCollector;
class nsAccessible;
class nsAccEvent;
struct nsRoleMapEntry;
@ -256,6 +257,21 @@ public:
*/
PRBool HasChildren() { return !!GetChildAt(0); }
/**
* Return embedded accessible children count.
*/
PRInt32 GetEmbeddedChildCount();
/**
* Return embedded accessible child at the given index.
*/
nsAccessible* GetEmbeddedChildAt(PRUint32 aIndex);
/**
* Return index of the given embedded accessible child.
*/
PRInt32 GetIndexOfEmbeddedChild(nsAccessible* aChild);
/**
* Return cached accessible of parent-child relatives.
*/
@ -271,7 +287,7 @@ public:
mParent->mChildren.SafeElementAt(mIndexInParent - 1, nsnull).get() : nsnull;
}
PRUint32 GetCachedChildCount() const { return mChildren.Length(); }
PRBool AreChildrenCached() const { return mAreChildrenInitialized; }
PRBool AreChildrenCached() const { return mChildrenFlags != eChildrenUninitialized; }
#ifdef DEBUG
/**
@ -443,9 +459,19 @@ protected:
// Data Members
nsRefPtr<nsAccessible> mParent;
nsTArray<nsRefPtr<nsAccessible> > mChildren;
PRBool mAreChildrenInitialized;
PRInt32 mIndexInParent;
enum ChildrenFlags {
eChildrenUninitialized = 0x00,
eMixedChildren = 0x01,
eEmbeddedChildren = 0x02
};
ChildrenFlags mChildrenFlags;
nsAutoPtr<EmbeddedObjCollector> mEmbeddedObjCollector;
PRInt32 mIndexOfEmbeddedChild;
friend class EmbeddedObjCollector;
nsAutoPtr<AccGroupInfo> mGroupInfo;
friend class AccGroupInfo;

View File

@ -191,7 +191,7 @@ nsOuterDocAccessible::InvalidateChildren()
// then allow nsAccDocManager to handle this case since the document
// accessible is created and appended as a child when it's requested.
mAreChildrenInitialized = PR_FALSE;
mChildrenFlags = eChildrenUninitialized;
}
PRBool

View File

@ -1091,11 +1091,8 @@ nsHyperTextAccessible::GetTextAttributes(PRBool aIncludeDefAttrs,
NS_ADDREF(*aAttributes = attributes);
}
PRInt32 offsetAccIdx = -1;
PRInt32 startOffset = 0, endOffset = 0;
nsAccessible *offsetAcc = GetAccessibleAtOffset(aOffset, &offsetAccIdx,
&startOffset, &endOffset);
if (!offsetAcc) {
nsAccessible* accAtOffset = GetChildAtOffset(aOffset);
if (!accAtOffset) {
// Offset 0 is correct offset when accessible has empty text. Include
// default attributes if they were requested, otherwise return empty set.
if (aOffset == 0) {
@ -1108,17 +1105,21 @@ nsHyperTextAccessible::GetTextAttributes(PRBool aIncludeDefAttrs,
return NS_ERROR_INVALID_ARG;
}
PRInt32 accAtOffsetIdx = accAtOffset->GetIndexInParent();
PRInt32 startOffset = GetChildOffset(accAtOffsetIdx);
PRInt32 endOffset = GetChildOffset(accAtOffsetIdx + 1);
PRInt32 offsetInAcc = aOffset - startOffset;
nsTextAttrsMgr textAttrsMgr(this, aIncludeDefAttrs, offsetAcc, offsetAccIdx);
nsTextAttrsMgr textAttrsMgr(this, aIncludeDefAttrs, accAtOffset,
accAtOffsetIdx);
nsresult rv = textAttrsMgr.GetAttributes(*aAttributes, &startOffset,
&endOffset);
NS_ENSURE_SUCCESS(rv, rv);
// Compute spelling attributes on text accessible only.
nsIFrame *offsetFrame = offsetAcc->GetFrame();
nsIFrame *offsetFrame = accAtOffset->GetFrame();
if (offsetFrame && offsetFrame->GetType() == nsAccessibilityAtoms::textFrame) {
nsCOMPtr<nsIDOMNode> node = offsetAcc->GetDOMNode();
nsCOMPtr<nsIDOMNode> node = accAtOffset->GetDOMNode();
PRInt32 nodeOffset = 0;
nsresult rv = RenderedToContentOffset(offsetFrame, offsetInAcc,
@ -1369,7 +1370,7 @@ nsHyperTextAccessible::GetLinkIndex(nsIAccessibleHyperLink* aLink,
}
NS_IMETHODIMP
nsHyperTextAccessible::GetLinkIndexAtOffset(PRInt32 aCharIndex,
nsHyperTextAccessible::GetLinkIndexAtOffset(PRInt32 aOffset,
PRInt32* aLinkIndex)
{
NS_ENSURE_ARG_POINTER(aLinkIndex);
@ -1378,29 +1379,7 @@ nsHyperTextAccessible::GetLinkIndexAtOffset(PRInt32 aCharIndex,
if (IsDefunct())
return NS_ERROR_FAILURE;
PRInt32 characterCount = 0;
PRInt32 linkIndex = 0;
PRInt32 childCount = GetChildCount();
for (PRInt32 childIdx = 0;
childIdx < childCount && characterCount <= aCharIndex; childIdx++) {
nsAccessible *childAcc = mChildren[childIdx];
PRUint32 role = nsAccUtils::Role(childAcc);
if (role == nsIAccessibleRole::ROLE_TEXT_LEAF ||
role == nsIAccessibleRole::ROLE_STATICTEXT) {
characterCount += nsAccUtils::TextLength(childAcc);
}
else {
if (characterCount ++ == aCharIndex) {
*aLinkIndex = linkIndex;
break;
}
if (role != nsIAccessibleRole::ROLE_WHITESPACE) {
++ linkIndex;
}
}
}
*aLinkIndex = GetLinkIndexAtOffset(aOffset);
return NS_OK;
}
@ -2043,7 +2022,6 @@ nsHyperTextAccessible::ScrollSubstringToPoint(PRInt32 aStartIndex,
void
nsHyperTextAccessible::InvalidateChildren()
{
mLinks = nsnull;
mOffsets.Clear();
nsAccessibleWrap::InvalidateChildren();
@ -2110,70 +2088,77 @@ nsresult nsHyperTextAccessible::RenderedToContentOffset(nsIFrame *aFrame, PRUint
// nsHyperTextAccessible public
PRInt32
nsHyperTextAccessible::GetChildOffset(nsAccessible* aChild,
nsHyperTextAccessible::GetChildOffset(PRUint32 aChildIndex,
PRBool aInvalidateAfter)
{
PRInt32 index = GetIndexOf(aChild);
if (index == -1 || index == 0)
return index;
if (aChildIndex == 0)
return aChildIndex;
PRInt32 count = mOffsets.Length() - index;
PRInt32 count = mOffsets.Length() - aChildIndex;
if (count > 0) {
if (aInvalidateAfter)
mOffsets.RemoveElementsAt(index, count);
mOffsets.RemoveElementsAt(aChildIndex, count);
return mOffsets[index - 1];
return mOffsets[aChildIndex - 1];
}
PRUint32 lastOffset = mOffsets.IsEmpty() ?
0 : mOffsets[mOffsets.Length() - 1];
EnsureChildren();
while (mOffsets.Length() < index) {
while (mOffsets.Length() < aChildIndex) {
nsAccessible* child = mChildren[mOffsets.Length()];
lastOffset += nsAccUtils::TextLength(child);
mOffsets.AppendElement(lastOffset);
}
return mOffsets[index - 1];
}
////////////////////////////////////////////////////////////////////////////////
// nsHyperTextAccessible protected
AccCollector*
nsHyperTextAccessible::GetLinkCollector()
{
if (IsDefunct())
return nsnull;
if (!mLinks)
mLinks = new AccCollector(this, filters::GetEmbeddedObject);
return mLinks;
return mOffsets[aChildIndex - 1];
}
nsAccessible *
nsHyperTextAccessible::GetAccessibleAtOffset(PRInt32 aOffset, PRInt32 *aAccIdx,
PRInt32 *aStartOffset,
PRInt32 *aEndOffset)
PRInt32
nsHyperTextAccessible::GetChildIndexAtOffset(PRUint32 aOffset)
{
PRInt32 startOffset = 0, endOffset = 0;
PRInt32 childCount = GetChildCount();
for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
nsAccessible *child = mChildren[childIdx];
endOffset += nsAccUtils::TextLength(child);
if (endOffset > aOffset) {
*aStartOffset = startOffset;
*aEndOffset = endOffset;
*aAccIdx = childIdx;
return child;
PRUint32 lastOffset = 0;
PRUint32 offsetCount = mOffsets.Length();
if (offsetCount > 0) {
lastOffset = mOffsets[offsetCount - 1];
if (aOffset < lastOffset) {
PRUint32 low = 0, high = offsetCount;
while (high > low) {
PRUint32 mid = (high + low) >> 1;
if (mOffsets[mid] == aOffset)
return mid < offsetCount - 1 ? mid + 1 : mid;
if (mOffsets[mid] < aOffset)
low = mid + 1;
else
high = mid;
}
if (high == offsetCount)
return -1;
return low < offsetCount - 1 ? low + 1 : low;
}
startOffset = endOffset;
}
return nsnull;
PRUint32 childCount = GetChildCount();
while (mOffsets.Length() < childCount) {
nsAccessible* child = GetChildAt(mOffsets.Length());
lastOffset += nsAccUtils::TextLength(child);
mOffsets.AppendElement(lastOffset);
if (aOffset < lastOffset)
return mOffsets.Length() - 1;
}
if (aOffset == lastOffset)
return mOffsets.Length() - 1;
return -1;
}
////////////////////////////////////////////////////////////////////////////////
// nsHyperTextAccessible protected
nsresult
nsHyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame *aFrame,
PRInt32 aOffset,

View File

@ -106,8 +106,7 @@ public:
*/
inline PRUint32 GetLinkCount()
{
AccCollector* links = GetLinkCollector();
return links ? links->Count() : 0;
return GetEmbeddedChildCount();
}
/**
@ -115,8 +114,7 @@ public:
*/
inline nsAccessible* GetLinkAt(PRUint32 aIndex)
{
AccCollector* links = GetLinkCollector();
return links ? links->GetAccessibleAt(aIndex) : nsnull;
return GetEmbeddedChildAt(aIndex);
}
/**
@ -124,8 +122,16 @@ public:
*/
inline PRInt32 GetLinkIndex(nsAccessible* aLink)
{
AccCollector* links = GetLinkCollector();
return links ? links->GetIndexAt(aLink) : -1;
return GetIndexOfEmbeddedChild(aLink);
}
/**
* Return link accessible at the given text offset.
*/
inline PRInt32 GetLinkIndexAtOffset(PRUint32 aOffset)
{
nsAccessible* child = GetChildAtOffset(aOffset);
return GetLinkIndex(child);
}
/**
@ -187,23 +193,46 @@ public:
PRInt32 *aEndOffset);
/**
* Return text offset the given child accessible of hypertext accessible.
* Return text offset of the given child accessible within hypertext
* accessible.
*
* @param aChild [in] accessible child to get text offset for
* @param aInvalidateAfter [in, optional] indicates whether invalidate
* cached offsets for next siblings of the child
*/
PRInt32 GetChildOffset(nsAccessible* aChild,
PRBool aInvalidateAfter = PR_FALSE)
{
PRInt32 index = GetIndexOf(aChild);
return index == -1 ? -1 : GetChildOffset(index, aInvalidateAfter);
}
/**
* Return text offset for the child accessible index.
*/
PRInt32 GetChildOffset(PRUint32 aChildIndex,
PRBool aInvalidateAfter = PR_FALSE);
/**
* Return child accessible at the given text offset.
*
* @param aOffset [in] the given text offset
*/
PRInt32 GetChildIndexAtOffset(PRUint32 aOffset);
/**
* Return child accessible at the given text offset.
*
* @param aOffset [in] the given text offset
*/
nsAccessible* GetChildAtOffset(PRUint32 aOffset)
{
return GetChildAt(GetChildIndexAtOffset(aOffset));
}
protected:
// nsHyperTextAccessible
/**
* Return link collection, create it if necessary.
*/
AccCollector* GetLinkCollector();
/*
* This does the work for nsIAccessibleText::GetText[At|Before|After]Offset
* @param aType, eGetBefore, eGetAt, eGetAfter
@ -299,18 +328,6 @@ protected:
*/
PRInt32 GetCaretLineNumber();
/**
* Return an accessible at the given hypertext offset.
*
* @param aOffset [out] the given hypertext offset
* @param aAccIdx [out] child index of returned accessible
* @param aStartOffset [out] start hypertext offset of returned accessible
* @param aEndOffset [out] end hypertext offset of returned accessible
*/
nsAccessible *GetAccessibleAtOffset(PRInt32 aOffset, PRInt32 *aAccIdx,
PRInt32 *aStartOffset,
PRInt32 *aEndOffset);
// Helpers
nsresult GetDOMPointByFrameOffset(nsIFrame *aFrame, PRInt32 aOffset,
nsIAccessible *aAccessible,

View File

@ -117,16 +117,11 @@ CAccessibleHypertext::get_hyperlinkIndex(long aCharIndex, long *aHyperlinkIndex)
__try {
*aHyperlinkIndex = 0;
nsCOMPtr<nsIAccessibleHyperText> hyperAcc(do_QueryObject(this));
nsRefPtr<nsHyperTextAccessible> hyperAcc(do_QueryObject(this));
if (!hyperAcc)
return E_FAIL;
PRInt32 index = 0;
nsresult rv = hyperAcc->GetLinkIndexAtOffset(aCharIndex, &index);
if (NS_FAILED(rv))
return GetHRESULT(rv);
*aHyperlinkIndex = index;
*aHyperlinkIndex = hyperAcc->GetLinkIndexAtOffset(aCharIndex);
return S_OK;
} __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }

View File

@ -428,8 +428,12 @@
ID = "area14";
defAttrs = buildDefaultTextAttrs(ID, kInputFontSize);
attrs = { };
testTextAttrs(ID, 0, attrs, defAttrs, 0, 0);
// XXX: While we expose text leaf accessibles for placeholder we grab its
// style, bug 545817.
// attrs = { color: "rgb(109, 109, 109)" };
//testTextAttrs(ID, 0, attrs, defAttrs, 0, 0);
todo(false, "enable commented tests when bug 545817 is fixed");
//////////////////////////////////////////////////////////////////////////
// area15, embed char tests, "*plain*plain**bold*bold*"