Bug 1011496 - complete the TextRange interface design, r=davidb

This commit is contained in:
Alexander Surkov 2014-05-22 20:10:19 -04:00
parent b8e23eaf85
commit 4c1b6b9f6f
10 changed files with 904 additions and 59 deletions

View File

@ -6,20 +6,149 @@
#include "nsISupports.idl"
interface nsIAccessible;
interface nsIAccessibleText;
interface nsIArray;
interface nsIVariant;
/**
* A range representing a piece of text in the document.
*/
[scriptable, uuid(6fe17c33-6709-4d7a-9ba0-3d448c4b3ef4)]
[scriptable, uuid(525b3401-8a67-4822-b35d-661065767cd8)]
interface nsIAccessibleTextRange : nsISupports
{
readonly attribute nsIAccessible startContainer;
readonly attribute nsIAccessibleText startContainer;
readonly attribute long startOffset;
readonly attribute nsIAccessible endContainer;
readonly attribute nsIAccessibleText endContainer;
readonly attribute long endOffset;
/**
* Return an accessible containing the whole range
*/
readonly attribute nsIAccessible container;
/**
* Return embedded children within the range.
*/
readonly attribute nsIArray embeddedChildren;
/**
* Return true if this range has the same end points of the given range.
*/
boolean compare(in nsIAccessibleTextRange aOtherRange);
/**
* The two endpoints of the range (starting and ending).
*/
const unsigned long EndPoint_Start = 1;
const unsigned long EndPoint_End = 2;
/**
* Compare this and given ranges end points.
*
* @return -1/0/1 if this range end point is before/equal/after the given
* range end point.
*/
long compareEndPoints(in unsigned long aEndPoint,
in nsIAccessibleTextRange aOtherRange,
in unsigned long aOtherRangeEndPoint);
/**
* Return text within the range.
*/
readonly attribute AString text;
/**
* Return list of rects of the range.
*/
readonly attribute nsIArray bounds;
const unsigned long FormatUnit = 0;
const unsigned long WordUnit = 1;
const unsigned long LineUnit = 2;
const unsigned long ParagraphUnit = 3;
const unsigned long PageUnit = 4;
const unsigned long DocumentUnit = 5;
/**
* Move the boundary(ies) by the given number of the unit.
*/
void move(in unsigned long aUnit, in long aCount);
void moveStart(in unsigned long aUnit, in long aCount);
void moveEnd(in unsigned long aUnit, in long aCount);
/**
* Normalize the range to the closest unit of the given type.
*/
void normalize(in unsigned long aUnit);
/**
* Return range enclosing the found text.
*/
nsIAccessibleTextRange findText(in AString aText, in boolean aIsBackward,
in boolean aIsIgnoreCase);
/**
* Text attributes. Used in conjunction with findAttrs().
*/
const unsigned long AnimationStyleAttr = 0;
const unsigned long AnnotationObjectsAttr = 1;
const unsigned long AnnotationTypesAttr = 2;
const unsigned long BackgroundColorAttr = 3;
const unsigned long BulletStyleAttr = 4;
const unsigned long CapStyleAttr = 5;
const unsigned long CaretBidiModeAttr = 6;
const unsigned long CaretPositionAttr = 7;
const unsigned long CultureAttr = 8;
const unsigned long FontNameAttr = 9;
const unsigned long FontSizeAttr = 10;
const unsigned long FontWeightAttr = 11;
const unsigned long ForegroundColorAttr = 12;
const unsigned long HorizontalTextAlignmentAttr = 13;
const unsigned long IndentationFirstLineAttr = 14;
const unsigned long IndentationLeadingAttr = 15;
const unsigned long IndentationTrailingAttr = 16;
const unsigned long IsActiveAttr = 17;
const unsigned long IsHiddenAttr = 18;
const unsigned long IsItalicAttr = 19;
const unsigned long IsReadOnlyAttr = 20;
const unsigned long IsSubscriptAttr = 21;
const unsigned long IsSuperscriptAttr = 22;
const unsigned long LinkAttr = 23;
const unsigned long MarginBottomAttr = 24;
const unsigned long MarginLeadingAttr = 25;
const unsigned long MarginTopAttr = 26;
const unsigned long MarginTrailingAttr = 27;
const unsigned long OutlineStylesAttr = 28;
const unsigned long OverlineColorAttr = 29;
const unsigned long OverlineStyleAttr = 30;
const unsigned long SelectionActiveEndAttr = 31;
const unsigned long StrikethroughColorAttr = 32;
const unsigned long StrikethroughStyleAttr = 33;
const unsigned long StyleIdAttr = 34;
const unsigned long StyleNameAttr = 35;
const unsigned long TabsAttr = 36;
const unsigned long TextFlowDirectionsAttr = 37;
const unsigned long UnderlineColorAttr = 38;
const unsigned long UnderlineStyleAttr = 39;
/**
* Return range enslosing the text having requested attribute.
*/
nsIAccessibleTextRange findAttr(in unsigned long aAttr, in nsIVariant aValue,
in boolean aIsBackward);
/**
* Add/remove the text range from selection.
*/
void addToSelection();
void removeFromSelection();
void select();
const unsigned long AlignToTop = 0;
const unsigned long AlignToBottom = 1;
/**
* Scroll the range into view.
*/
void scrollIntoView(in unsigned long aHow);
};

View File

@ -6,28 +6,239 @@
#include "TextRange.h"
#include "Accessible-inl.h"
#include "HyperTextAccessible.h"
#include "nsAccUtils.h"
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// TextPoint
bool
TextPoint::operator <(const TextPoint& aPoint) const
{
if (mContainer == aPoint.mContainer)
return mOffset < aPoint.mOffset;
// Build the chain of parents
Accessible* p1 = mContainer;
Accessible* p2 = aPoint.mContainer;
nsAutoTArray<Accessible*, 30> parents1, parents2;
do {
parents1.AppendElement(p1);
p1 = p1->Parent();
} while (p1);
do {
parents2.AppendElement(p2);
p2 = p2->Parent();
} while (p2);
// Find where the parent chain differs
uint32_t pos1 = parents1.Length(), pos2 = parents2.Length();
for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
Accessible* child1 = parents1.ElementAt(--pos1);
Accessible* child2 = parents2.ElementAt(--pos2);
if (child1 != child2)
return child1->IndexInParent() < child2->IndexInParent();
}
NS_ERROR("Broken tree?!");
return false;
}
////////////////////////////////////////////////////////////////////////////////
// TextRange
TextRange::TextRange(HyperTextAccessible* aRoot,
Accessible* aStartContainer, int32_t aStartOffset,
Accessible* aEndContainer, int32_t aEndOffset) :
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
HyperTextAccessible* aEndContainer, int32_t aEndOffset) :
mRoot(aRoot), mStartContainer(aStartContainer), mEndContainer(aEndContainer),
mStartOffset(aStartOffset), mEndOffset(aEndOffset)
{
}
Accessible*
TextRange::Container() const
{
if (mStartContainer == mEndContainer)
return mStartContainer;
// Build the chain of parents
Accessible* p1 = mStartContainer;
Accessible* p2 = mEndContainer;
nsAutoTArray<Accessible*, 30> parents1, parents2;
do {
parents1.AppendElement(p1);
p1 = p1->Parent();
} while (p1);
do {
parents2.AppendElement(p2);
p2 = p2->Parent();
} while (p2);
// Find where the parent chain differs
uint32_t pos1 = parents1.Length();
uint32_t pos2 = parents2.Length();
Accessible* parent = nullptr;
uint32_t len = 0;
for (len = std::min(pos1, pos2); len > 0; --len) {
Accessible* child1 = parents1.ElementAt(--pos1);
Accessible* child2 = parents2.ElementAt(--pos2);
if (child1 != child2)
break;
parent = child1;
}
return parent;
}
void
TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const
{
if (mStartContainer == mEndContainer) {
int32_t startIdx = mStartContainer->GetChildIndexAtOffset(mStartOffset);
int32_t endIdx = mStartContainer->GetChildIndexAtOffset(mEndOffset);
for (int32_t idx = startIdx; idx <= endIdx; idx++) {
Accessible* child = mStartContainer->GetChildAt(idx);
if (nsAccUtils::IsEmbeddedObject(child))
aChildren->AppendElement(child);
}
return;
}
Accessible* p1 = mStartContainer->GetChildAtOffset(mStartOffset);
Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset);
nsAutoTArray<Accessible*, 30> parents1, parents2;
do {
parents1.AppendElement(p1);
p1 = p1->Parent();
} while (p1);
do {
parents2.AppendElement(p2);
p2 = p2->Parent();
} while (p2);
// Find deepest common container.
uint32_t pos1 = parents1.Length();
uint32_t pos2 = parents2.Length();
Accessible* container = nullptr;
for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
Accessible* child1 = parents1.ElementAt(--pos1);
Accessible* child2 = parents2.ElementAt(--pos2);
if (child1 != child2)
break;
container = child1;
}
// Traverse the tree up to the container and collect embedded objects.
for (uint32_t idx = 0; idx < pos1 - 1; idx++) {
Accessible* parent = parents1[idx + 1];
Accessible* child = parents1[idx];
uint32_t childCount = parent->ChildCount();
for (uint32_t childIdx = child->IndexInParent(); childIdx < childCount; childIdx++) {
Accessible* next = parent->GetChildAt(childIdx);
if (nsAccUtils::IsEmbeddedObject(next))
aChildren->AppendElement(next);
}
}
// Traverse through direct children in the container.
int32_t endIdx = parents2[pos2 - 1]->IndexInParent();
int32_t childIdx = parents1[pos1 - 1]->IndexInParent() + 1;
for (; childIdx < endIdx; childIdx++) {
Accessible* next = container->GetChildAt(childIdx);
if (nsAccUtils::IsEmbeddedObject(next))
aChildren->AppendElement(next);
}
// Traverse down from the container to end point.
for (int32_t idx = pos2 - 2; idx > 0; idx--) {
Accessible* parent = parents2[idx];
Accessible* child = parents2[idx - 1];
int32_t endIdx = child->IndexInParent();
for (int32_t childIdx = 0; childIdx < endIdx; childIdx++) {
Accessible* next = parent->GetChildAt(childIdx);
if (nsAccUtils::IsEmbeddedObject(next))
aChildren->AppendElement(next);
}
}
}
void
TextRange::Text(nsAString& aText) const
{
Accessible* current = mStartContainer->GetChildAtOffset(mStartOffset);
uint32_t startIntlOffset =
mStartOffset - mStartContainer->GetChildOffset(current);
while (current && TextInternal(aText, current, startIntlOffset)) {
current = current->Parent();
if (!current)
break;
current = current->NextSibling();
}
}
void
TextRange::Bounds(nsTArray<nsIntRect> aRects) const
{
}
void
TextRange::Normalize(ETextUnit aUnit)
{
}
void
TextRange::FindText(const nsAString& aText, EDirection aDirection,
nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const
{
}
void
TextRange::FindAttr(EAttr aAttr, nsIVariant* aValue, EDirection aDirection,
TextRange* aFoundRange) const
{
}
void
TextRange::AddToSelection() const
{
}
void
TextRange::RemoveFromSelection() const
{
}
void
TextRange::Select() const
{
}
void
TextRange::ScrollIntoView(EHowToAlign aHow) const
{
}
////////////////////////////////////////////////////////////////////////////////
// pivate
void
TextRange::Set(HyperTextAccessible* aRoot,
Accessible* aStartContainer, int32_t aStartOffset,
Accessible* aEndContainer, int32_t aEndOffset)
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
HyperTextAccessible* aEndContainer, int32_t aEndOffset)
{
mRoot = aRoot;
mStartContainer = aStartContainer;
@ -35,3 +246,51 @@ TextRange::Set(HyperTextAccessible* aRoot,
mStartOffset = aStartOffset;
mEndOffset = aEndOffset;
}
bool
TextRange::TextInternal(nsAString& aText, Accessible* aCurrent,
uint32_t aStartIntlOffset) const
{
bool moveNext = true;
int32_t endIntlOffset = -1;
if (aCurrent->Parent() == mEndContainer &&
mEndContainer->GetChildAtOffset(mEndOffset) == aCurrent) {
uint32_t currentStartOffset = mEndContainer->GetChildOffset(aCurrent);
endIntlOffset = mEndOffset - currentStartOffset;
if (endIntlOffset == 0)
return false;
moveNext = false;
}
if (aCurrent->IsTextLeaf()) {
aCurrent->AppendTextTo(aText, aStartIntlOffset,
endIntlOffset - aStartIntlOffset);
if (!moveNext)
return false;
}
Accessible* next = aCurrent->FirstChild();
if (next) {
if (!TextInternal(aText, next, 0))
return false;
}
next = aCurrent->NextSibling();
if (next) {
if (!TextInternal(aText, next, 0))
return false;
}
return moveNext;
}
void
TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount,
HyperTextAccessible& aContainer, int32_t aOffset,
HyperTextAccessible* aStopContainer, int32_t aStopOffset)
{
}

View File

@ -9,6 +9,11 @@
#include "mozilla/Move.h"
#include "nsAutoPtr.h"
#include "nsCaseTreatment.h"
#include "nsRect.h"
#include "nsTArray.h"
class nsIVariant;
namespace mozilla {
namespace a11y {
@ -16,6 +21,24 @@ namespace a11y {
class Accessible;
class HyperTextAccessible;
/**
* A text point (hyper text + offset), represents a boundary of text range.
*/
struct TextPoint MOZ_FINAL
{
TextPoint(HyperTextAccessible* aContainer, int32_t aOffset) :
mContainer(aContainer), mOffset(aOffset) { }
TextPoint(const TextPoint& aPoint) :
mContainer(aPoint.mContainer), mOffset(aPoint.mOffset) { }
HyperTextAccessible* mContainer;
int32_t mOffset;
bool operator ==(const TextPoint& aPoint) const
{ return mContainer == aPoint.mContainer && mOffset == aPoint.mOffset; }
bool operator <(const TextPoint& aPoint) const;
};
/**
* Represents a text range within the text control or document.
*/
@ -23,39 +46,177 @@ class TextRange MOZ_FINAL
{
public:
TextRange(HyperTextAccessible* aRoot,
Accessible* aStartContainer, int32_t aStartOffset,
Accessible* aEndContainer, int32_t aEndOffset);
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
HyperTextAccessible* aEndContainer, int32_t aEndOffset);
TextRange() {}
TextRange(TextRange&& aRange) :
mRoot(Move(aRange.mRoot)), mStartContainer(Move(aRange.mStartContainer)),
mEndContainer(Move(aRange.mEndContainer)),
mRoot(mozilla::Move(aRange.mRoot)),
mStartContainer(mozilla::Move(aRange.mStartContainer)),
mEndContainer(mozilla::Move(aRange.mEndContainer)),
mStartOffset(aRange.mStartOffset), mEndOffset(aRange.mEndOffset) {}
TextRange& operator= (TextRange&& aRange)
{
mRoot = Move(aRange.mRoot);
mStartContainer = Move(aRange.mStartContainer);
mEndContainer = Move(aRange.mEndContainer);
mRoot = mozilla::Move(aRange.mRoot);
mStartContainer = mozilla::Move(aRange.mStartContainer);
mEndContainer = mozilla::Move(aRange.mEndContainer);
mStartOffset = aRange.mStartOffset;
mEndOffset = aRange.mEndOffset;
return *this;
}
Accessible* StartContainer() const { return mStartContainer; }
HyperTextAccessible* StartContainer() const { return mStartContainer; }
int32_t StartOffset() const { return mStartOffset; }
Accessible* EndContainer() const { return mEndContainer; }
HyperTextAccessible* EndContainer() const { return mEndContainer; }
int32_t EndOffset() const { return mEndOffset; }
bool operator ==(const TextRange& aRange) const
{
return mStartContainer == aRange.mStartContainer &&
mStartOffset == aRange.mStartOffset &&
mEndContainer == aRange.mEndContainer && mEndOffset == aRange.mEndOffset;
}
TextPoint StartPoint() const { return TextPoint(mStartContainer, mStartOffset); }
TextPoint EndPoint() const { return TextPoint(mEndContainer, mEndOffset); }
/**
* Return a container containing both start and end points.
*/
Accessible* Container() const;
/**
* Return a list of embedded objects enclosed by the text range (includes
* partially overlapped objects).
*/
void EmbeddedChildren(nsTArray<Accessible*>* aChildren) const;
/**
* Return text enclosed by the range.
*/
void Text(nsAString& aText) const;
/**
* Return list of bounding rects of the text range by lines.
*/
void Bounds(nsTArray<nsIntRect> aRects) const;
enum ETextUnit {
eFormat,
eWord,
eLine,
eParagraph,
ePage,
eDocument
};
/**
* Move the range or its points on specified amount of given units.
*/
void Move(ETextUnit aUnit, int32_t aCount)
{
MoveEnd(aUnit, aCount);
MoveStart(aUnit, aCount);
}
void MoveStart(ETextUnit aUnit, int32_t aCount)
{
MoveInternal(aUnit, aCount, *mStartContainer, mStartOffset,
mEndContainer, mEndOffset);
}
void MoveEnd(ETextUnit aUnit, int32_t aCount)
{ MoveInternal(aUnit, aCount, *mEndContainer, mEndOffset); }
/**
* Move the range points to the closest unit boundaries.
*/
void Normalize(ETextUnit aUnit);
enum EDirection {
eBackward,
eForward
};
/**
* Return range enclosing the found text.
*/
void FindText(const nsAString& aText, EDirection aDirection,
nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const;
enum EAttr {
eAnimationStyleAttr,
eAnnotationObjectsAttr,
eAnnotationTypesAttr,
eBackgroundColorAttr,
eBulletStyleAttr,
eCapStyleAttr,
eCaretBidiModeAttr,
eCaretPositionAttr,
eCultureAttr,
eFontNameAttr,
eFontSizeAttr,
eFontWeightAttr,
eForegroundColorAttr,
eHorizontalTextAlignmentAttr,
eIndentationFirstLineAttr,
eIndentationLeadingAttr,
eIndentationTrailingAttr,
eIsActiveAttr,
eIsHiddenAttr,
eIsItalicAttr,
eIsReadOnlyAttr,
eIsSubscriptAttr,
eIsSuperscriptAttr,
eLinkAttr,
eMarginBottomAttr,
eMarginLeadingAttr,
eMarginTopAttr,
eMarginTrailingAttr,
eOutlineStylesAttr,
eOverlineColorAttr,
eOverlineStyleAttr,
eSelectionActiveEndAttr,
eStrikethroughColorAttr,
eStrikethroughStyleAttr,
eStyleIdAttr,
eStyleNameAttr,
eTabsAttr,
eTextFlowDirectionsAttr,
eUnderlineColorAttr,
eUnderlineStyleAttr
};
/**
* Return range enclosing text having requested attribute.
*/
void FindAttr(EAttr aAttr, nsIVariant* aValue, EDirection aDirection,
TextRange* aFoundRange) const;
/**
* Add/remove the text range from selection.
*/
void AddToSelection() const;
void RemoveFromSelection() const;
void Select() const;
/**
* Scroll the text range into view.
*/
enum EHowToAlign {
eAlignToTop,
eAlignToBottom
};
void ScrollIntoView(EHowToAlign aHow) const;
/**
* Return true if this TextRange object represents an actual range of text.
*/
bool IsValid() const { return mRoot; }
void SetStartPoint(HyperTextAccessible* aContainer, int32_t aOffset)
{ mStartContainer = aContainer; mStartOffset = aOffset; }
void SetEndPoint(HyperTextAccessible* aContainer, int32_t aOffset)
{ mStartContainer = aContainer; mStartOffset = aOffset; }
private:
TextRange(const TextRange& aRange) MOZ_DELETE;
TextRange& operator=(const TextRange& aRange) MOZ_DELETE;
@ -64,12 +225,27 @@ private:
friend class xpcAccessibleTextRange;
void Set(HyperTextAccessible* aRoot,
Accessible* aStartContainer, int32_t aStartOffset,
Accessible* aEndContainer, int32_t aEndOffset);
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
HyperTextAccessible* aEndContainer, int32_t aEndOffset);
/**
* Text() method helper.
* @param aText [in,out] calculated text
* @param aCurrent [in] currently traversed node
* @param aStartIntlOffset [in] start offset if current node is a text node
* @return true if calculation is not finished yet
*/
bool TextInternal(nsAString& aText, Accessible* aCurrent,
uint32_t aStartIntlOffset) const;
void MoveInternal(ETextUnit aUnit, int32_t aCount,
HyperTextAccessible& aContainer, int32_t aOffset,
HyperTextAccessible* aStopContainer = nullptr,
int32_t aStopOffset = 0);
nsRefPtr<HyperTextAccessible> mRoot;
nsRefPtr<Accessible> mStartContainer;
nsRefPtr<Accessible> mEndContainer;
nsRefPtr<HyperTextAccessible> mStartContainer;
nsRefPtr<HyperTextAccessible> mEndContainer;
int32_t mStartOffset;
int32_t mEndOffset;
};

View File

@ -120,7 +120,7 @@ Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
#ifdef NS_DEBUG_X
{
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aShell));
printf(">>> %p Created Acc - DOM: %p PS: %p",
printf(">>> %p Created Acc - DOM: %p PS: %p",
(void*)static_cast<nsIAccessible*>(this), (void*)aNode,
(void*)shell.get());
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
@ -267,7 +267,7 @@ Accessible::Description(nsString& aDescription)
// 1. it's a text node; or
// 2. It has no DHTML describedby property
// 3. it doesn't have an accName; or
// 4. its title attribute already equals to its accName nsAutoString name;
// 4. its title attribute already equals to its accName nsAutoString name;
if (!HasOwnContent() || mContent->IsNodeOfType(nsINode::eTEXT))
return;
@ -550,7 +550,7 @@ Accessible::GetIndexInParent(int32_t* aIndexInParent)
return *aIndexInParent != -1 ? NS_OK : NS_ERROR_FAILURE;
}
void
void
Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
{
nsCOMPtr<nsIStringBundleService> stringBundleService =
@ -560,7 +560,7 @@ Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
nsCOMPtr<nsIStringBundle> stringBundle;
stringBundleService->CreateBundle(
"chrome://global-platform/locale/accessible.properties",
"chrome://global-platform/locale/accessible.properties",
getter_AddRefs(stringBundle));
if (!stringBundle)
return;
@ -1104,7 +1104,7 @@ Accessible::XULElmName(DocAccessible* aDocument,
itemEl->GetLabel(aName);
} else {
nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
// Use label if this is not a select control element which
// Use label if this is not a select control element which
// uses label attribute to indicate which option is selected
if (!select) {
nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aElm));
@ -1578,7 +1578,7 @@ Accessible::ApplyARIAState(uint64_t* aState) const
*aState |= states::UNAVAILABLE;
break;
}
}
}
}
// special case: A native button element whose role got transformed by ARIA to a toggle button
@ -2451,7 +2451,7 @@ Accessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
void
Accessible::Shutdown()
{
// Mark the accessible as defunct, invalidate the child count and pointers to
// Mark the accessible as defunct, invalidate the child count and pointers to
// other accessibles, also make sure none of its children point to this parent
mStateFlags |= eIsDefunct;
@ -3058,7 +3058,7 @@ Accessible::TestChildCache(Accessible* aCachedChild) const
}
NS_ASSERTION(child == aCachedChild,
"[TestChildCache] cached accessible wasn't found. Wrong accessible tree!");
"[TestChildCache] cached accessible wasn't found. Wrong accessible tree!");
#endif
}
@ -3135,7 +3135,7 @@ Accessible::GetActionRule()
if (isOnclick)
return eClickAction;
// Get an action based on ARIA role.
if (mRoleMapEntry &&
mRoleMapEntry->actionRule != eNoAction)

View File

@ -1551,9 +1551,9 @@ HyperTextAccessible::EnclosingRange(a11y::TextRange& aRange) const
{
if (IsTextField()) {
aRange.Set(mDoc, const_cast<HyperTextAccessible*>(this), 0,
const_cast<HyperTextAccessible*>(this), ChildCount());
const_cast<HyperTextAccessible*>(this), CharacterCount());
} else {
aRange.Set(mDoc, mDoc, 0, mDoc, mDoc->ChildCount());
aRange.Set(mDoc, mDoc, 0, mDoc, mDoc->CharacterCount());
}
}
@ -1599,7 +1599,26 @@ void
HyperTextAccessible::RangeByChild(Accessible* aChild,
a11y::TextRange& aRange) const
{
aRange.Set(mDoc, aChild, 0, aChild, aChild->ChildCount());
HyperTextAccessible* ht = aChild->AsHyperText();
if (ht) {
aRange.Set(mDoc, ht, 0, ht, ht->CharacterCount());
return;
}
Accessible* child = aChild;
Accessible* parent = nullptr;
while ((parent = child->Parent()) && !(ht = parent->AsHyperText()))
child = parent;
// If no text then return collapsed text range, otherwise return a range
// containing the text enclosed by the given child.
if (ht) {
int32_t childIdx = child->IndexInParent();
int32_t startOffset = ht->GetChildOffset(childIdx);
int32_t endOffset = child->IsTextLeaf() ?
ht->GetChildOffset(childIdx + 1) : startOffset;
aRange.Set(mDoc, ht, startOffset, ht, endOffset);
}
}
void
@ -1607,8 +1626,19 @@ HyperTextAccessible::RangeAtPoint(int32_t aX, int32_t aY,
a11y::TextRange& aRange) const
{
Accessible* child = mDoc->ChildAtPoint(aX, aY, eDeepestChild);
if (child)
aRange.Set(mDoc, child, 0, child, child->ChildCount());
if (!child)
return;
Accessible* parent = nullptr;
while ((parent = child->Parent()) && !parent->IsHyperText())
child = parent;
// Return collapsed text range for the point.
if (parent) {
HyperTextAccessible* ht = parent->AsHyperText();
int32_t offset = ht->GetChildOffset(child);
aRange.Set(mDoc, ht, offset, ht, offset);
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -9,6 +9,8 @@
#include "HyperTextAccessible.h"
#include "TextRange.h"
#include "nsIMutableArray.h"
using namespace mozilla;
using namespace mozilla::a11y;
@ -21,6 +23,7 @@ NS_IMPL_CYCLE_COLLECTION(xpcAccessibleTextRange,
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(xpcAccessibleTextRange)
NS_INTERFACE_MAP_ENTRY(nsIAccessibleTextRange)
NS_INTERFACE_MAP_ENTRY(xpcAccessibleTextRange)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleTextRange)
NS_INTERFACE_MAP_END
@ -30,10 +33,10 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(xpcAccessibleTextRange)
// nsIAccessibleTextRange
NS_IMETHODIMP
xpcAccessibleTextRange::GetStartContainer(nsIAccessible** aAnchor)
xpcAccessibleTextRange::GetStartContainer(nsIAccessibleText** aAnchor)
{
NS_ENSURE_ARG_POINTER(aAnchor);
NS_IF_ADDREF(*aAnchor = static_cast<nsIAccessible*>(mRange.StartContainer()));
NS_IF_ADDREF(*aAnchor = static_cast<nsIAccessibleText*>(mRange.StartContainer()));
return NS_OK;
}
@ -46,10 +49,10 @@ xpcAccessibleTextRange::GetStartOffset(int32_t* aOffset)
}
NS_IMETHODIMP
xpcAccessibleTextRange::GetEndContainer(nsIAccessible** aAnchor)
xpcAccessibleTextRange::GetEndContainer(nsIAccessibleText** aAnchor)
{
NS_ENSURE_ARG_POINTER(aAnchor);
NS_IF_ADDREF(*aAnchor = static_cast<nsIAccessible*>(mRange.EndContainer()));
NS_IF_ADDREF(*aAnchor = static_cast<nsIAccessibleText*>(mRange.EndContainer()));
return NS_OK;
}
@ -61,6 +64,70 @@ xpcAccessibleTextRange::GetEndOffset(int32_t* aOffset)
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::GetContainer(nsIAccessible** aContainer)
{
NS_ENSURE_ARG_POINTER(aContainer);
NS_IF_ADDREF(*aContainer = static_cast<nsIAccessible*>(mRange.Container()));
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::GetEmbeddedChildren(nsIArray** aList)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIMutableArray> xpcList =
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<Accessible*> objects;
mRange.EmbeddedChildren(&objects);
uint32_t len = objects.Length();
for (uint32_t idx = 0; idx < len; idx++)
xpcList->AppendElement(static_cast<nsIAccessible*>(objects[idx]), false);
xpcList.forget(aList);
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::Compare(nsIAccessibleTextRange* aOtherRange,
bool* aResult)
{
nsRefPtr<xpcAccessibleTextRange> xpcRange(do_QueryObject(aOtherRange));
if (!xpcRange || !aResult)
return NS_ERROR_INVALID_ARG;
*aResult = (mRange == xpcRange->mRange);
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::CompareEndPoints(uint32_t aEndPoint,
nsIAccessibleTextRange* aOtherRange,
uint32_t aOtherRangeEndPoint,
int32_t* aResult)
{
nsRefPtr<xpcAccessibleTextRange> xpcRange(do_QueryObject(aOtherRange));
if (!xpcRange || !aResult)
return NS_ERROR_INVALID_ARG;
TextPoint p = (aEndPoint == EndPoint_Start) ?
mRange.StartPoint() : mRange.EndPoint();
TextPoint otherPoint = (aOtherRangeEndPoint == EndPoint_Start) ?
xpcRange->mRange.StartPoint() : xpcRange->mRange.EndPoint();
if (p == otherPoint)
*aResult = 0;
else
*aResult = p < otherPoint ? -1 : 1;
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::GetText(nsAString& aText)
{
@ -70,3 +137,73 @@ xpcAccessibleTextRange::GetText(nsAString& aText)
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::GetBounds(nsIArray** aRectList)
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::Move(uint32_t aUnit, int32_t aCount)
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::MoveStart(uint32_t aUnit, int32_t aCount)
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::MoveEnd(uint32_t aUnit, int32_t aCount)
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::Normalize(uint32_t aUnit)
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::FindText(const nsAString& aText, bool aIsBackward,
bool aIsIgnoreCase,
nsIAccessibleTextRange** aRange)
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::FindAttr(uint32_t aAttr, nsIVariant* aVal,
bool aIsBackward,
nsIAccessibleTextRange** aRange)
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::AddToSelection()
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::RemoveFromSelection()
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::Select()
{
return NS_OK;
}
NS_IMETHODIMP
xpcAccessibleTextRange::ScrollIntoView(uint32_t aHow)
{
return NS_OK;
}

View File

@ -18,17 +18,47 @@ namespace a11y {
class TextRange;
#define NS_ACCESSIBLETEXTRANGE_IMPL_IID \
{ /* 133c8bf4-4913-4355-bd50-426bd1d6e1ad */ \
0xb17652d9, \
0x4f54, \
0x4c56, \
{ 0xbb, 0x62, 0x6d, 0x5b, 0xf1, 0xef, 0x91, 0x0c } \
}
class xpcAccessibleTextRange MOZ_FINAL : public nsIAccessibleTextRange
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(xpcAccessibleTextRange)
NS_IMETHOD GetStartContainer(nsIAccessible** aAnchor) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetStartContainer(nsIAccessibleText** aAnchor) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetStartOffset(int32_t* aOffset) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetEndContainer(nsIAccessible** aAnchor) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetEndContainer(nsIAccessibleText** aAnchor) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetEndOffset(int32_t* aOffset) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetContainer(nsIAccessible** aContainer) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetEmbeddedChildren(nsIArray** aList) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD Compare(nsIAccessibleTextRange* aOtherRange, bool* aResult) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD CompareEndPoints(uint32_t aEndPoint,
nsIAccessibleTextRange* aOtherRange,
uint32_t aOtherRangeEndPoint,
int32_t* aResult) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetText(nsAString& aText) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD GetBounds(nsIArray** aRectList) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD Move(uint32_t aUnit, int32_t aCount) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD MoveStart(uint32_t aUnit, int32_t aCount) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD MoveEnd(uint32_t aUnit, int32_t aCount) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD Normalize(uint32_t aUnit) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD FindText(const nsAString& aText, bool aIsBackward, bool aIsIgnoreCase,
nsIAccessibleTextRange** aRange) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD FindAttr(uint32_t aAttr, nsIVariant* aVal, bool aIsBackward,
nsIAccessibleTextRange** aRange) MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD AddToSelection() MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD RemoveFromSelection() MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD Select() MOZ_FINAL MOZ_OVERRIDE;
NS_IMETHOD ScrollIntoView(uint32_t aHow) MOZ_FINAL MOZ_OVERRIDE;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCESSIBLETEXTRANGE_IMPL_IID)
private:
xpcAccessibleTextRange(TextRange&& aRange) :
@ -42,6 +72,9 @@ private:
TextRange mRange;
};
NS_DEFINE_STATIC_IID_ACCESSOR(xpcAccessibleTextRange,
NS_ACCESSIBLETEXTRANGE_IMPL_IID)
} // namespace a11y
} // namespace mozilla

View File

@ -19,6 +19,7 @@ const nsIAccessibleScrollType = Components.interfaces.nsIAccessibleScrollType;
const nsIAccessibleCoordinateType = Components.interfaces.nsIAccessibleCoordinateType;
const nsIAccessibleRelation = Components.interfaces.nsIAccessibleRelation;
const nsIAccessibleTextRange = Components.interfaces.nsIAccessibleTextRange;
const nsIAccessible = Components.interfaces.nsIAccessible;
@ -434,7 +435,7 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags)
}
break;
}
}
default:
if (prop.indexOf("todo_") == 0)

View File

@ -10,6 +10,9 @@ const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END;
const kTextEndOffset = nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT;
const kCaretOffset = nsIAccessibleText.TEXT_OFFSET_CARET;
const EndPoint_Start = nsIAccessibleTextRange.EndPoint_Start;
const EndPoint_End = nsIAccessibleTextRange.EndPoint_End;
const kTodo = 1; // a test is expected to fail
const kOk = 2; // a test doesn't fail
@ -393,8 +396,8 @@ function testTextRemoveSelection(aID, aSelectionIndex, aSelectionsCount)
acc.removeSelection(aSelectionIndex);
ok(acc.selectionCount, aSelectionsCount,
text + ": failed to remove selection at index '" +
ok(acc.selectionCount, aSelectionsCount,
text + ": failed to remove selection at index '" +
aSelectionIndex + "': selectionCount after");
}
@ -416,8 +419,8 @@ function testTextSetSelection(aID, aStartOffset, aEndOffset,
acc.setSelectionBounds(aSelectionIndex, aStartOffset, aEndOffset);
is(acc.selectionCount, aSelectionsCount,
text + ": failed to set selection at index '" +
is(acc.selectionCount, aSelectionsCount,
text + ": failed to set selection at index '" +
aSelectionIndex + "': selectionCount after");
}
@ -457,17 +460,36 @@ function testTextGetSelection(aID, aStartOffset, aEndOffset, aSelectionIndex)
aSelectionIndex + "'");
}
function testTextRange(aRange, aStartContainer, aStartOffset,
aEndContainer, aEndOffset)
function testTextRange(aRange, aRangeDescr, aStartContainer, aStartOffset,
aEndContainer, aEndOffset, aText,
aCommonContainer, aChildren)
{
is(aRange.startContainer, getAccessible(aStartContainer),
"Wrong start container");
isObject(aRange.startContainer, getAccessible(aStartContainer),
"Wrong start container of " + aRangeDescr);
is(aRange.startOffset, aStartOffset,
"Wrong start offset");
is(aRange.endContainer, getAccessible(aEndContainer),
"Wrong end container");
"Wrong start offset of " + aRangeDescr);
isObject(aRange.endContainer, getAccessible(aEndContainer),
"Wrong end container of " + aRangeDescr);
is(aRange.endOffset, aEndOffset,
"Wrong end offset");
"Wrong end offset of " + aRangeDescr);
is(aRange.text, aText, "Wrong text of " + aRangeDescr);
var children = aRange.embeddedChildren;
is(children ? children.length : 0, aChildren ? aChildren.length : 0,
"Wrong embedded children count of " + aRangeDescr);
isObject(aRange.container, getAccessible(aCommonContainer),
"Wrong container of " + aRangeDescr);
if (aChildren) {
for (var i = 0; i < aChildren.length; i++) {
var expectedChild = getAccessible(aChildren[i]);
var actualChild = children.queryElementAt(i, nsIAccessible);
isObject(actualChild, expectedChild,
"Wrong child at index '" + i + "' of " + aRangeDescr);
}
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -11,19 +11,75 @@
src="../common.js"></script>
<script type="application/javascript"
src="../text.js"></script>
<script type="application/javascript"
src="../layout.js"></script>
<script type="application/javascript">
function doTest()
{
// enclosingRange
var input = getAccessible("input", [ nsIAccessibleText ]);
testTextRange(input.enclosingRange, input, 0, input, 1);
testTextRange(input.enclosingRange, "enclosing range for 'input'",
input, 0, input, 5, "hello", input);
var ta = getAccessible("textarea", [ nsIAccessibleText ]);
testTextRange(ta.enclosingRange, ta, 0, ta, 1);
testTextRange(ta.enclosingRange, "enclosing range for 'textarea'",
ta, 0, ta, 5, "hello", textarea);
var iframeDoc = getAccessible(getNode("iframe").contentDocument,
[ nsIAccessibleText ]);
testTextRange(iframeDoc.enclosingRange, iframeDoc, 0, iframeDoc, 1);
var iframeDocNode = getNode("iframe").contentDocument;
var iframeDoc = getAccessible(iframeDocNode, [ nsIAccessibleText ]);
testTextRange(iframeDoc.enclosingRange, "enclosing range for iframe doc",
iframeDoc, 0, iframeDoc, 1, "hello",
iframeDoc, [ getNode("p", iframeDocNode) ]);
// getRangeByChild
var docacc = getAccessible(document, [ nsIAccessibleText ]);
var p1 = getAccessible("p1");
var p1Range = docacc.getRangeByChild(p1);
testTextRange(p1Range, "range by 'p1' child",
p1, 0, "p1", 11, "text text",
p1, ["p1_img"]);
testTextRange(docacc.getRangeByChild(getAccessible("p1_img")),
"range by 'p1_img' child",
"p1", 5, "p1", 5, "",
"p1", ["p1_img"]);
var p2 = getAccessible("p2");
var p2Range = docacc.getRangeByChild(p2);
testTextRange(p2Range, "range by 'p2' child",
p2, 0, "p2", 11, "text link text",
p2, ["p2_a"]);
testTextRange(docacc.getRangeByChild(getAccessible("p2_a")),
"range by 'p2_a' child",
"p2_a", 0, "p2_a", 5, "link",
"p2_a", ["p2_img"]);
// getRangeAtPoint
getNode("p2_a").scrollIntoView(true);
var [x, y] = getPos("p2_a");
testTextRange(docacc.getRangeAtPoint(x + 1, y + 1),
"range at 'p2_a' top-left edge",
"p2_a", 0, "p2_a", 0, "",
"p2_a");
// TextRange::compare
ok(input.enclosingRange.compare(input.enclosingRange),
"input enclosing ranges should be equal");
ok(!input.enclosingRange.compare(ta.enclosingRange),
"input and textarea enclosing ranges can't be equal");
// TextRange::compareEndPoints
var res = p1Range.compareEndPoints(EndPoint_End, p2Range, EndPoint_Start);
is(res, -1, "p1 range must be lesser with p2 range");
res = p2Range.compareEndPoints(EndPoint_Start, p1Range, EndPoint_End);
is(res, 1, "p2 range must be greater with p1 range");
res = p1Range.compareEndPoints(EndPoint_Start, p1Range, EndPoint_Start);
is(res, 0, "p1 range must be equal with p1 range");
SimpleTest.finish();
}
@ -44,7 +100,9 @@
<input id="input" value="hello">
<textarea id="textarea">hello</textarea>
<iframe id="iframe" src="data:text/html,<p>hello</p>"></iframe>
<iframe id="iframe" src="data:text/html,<html><body><p id='p'>hello</p></body></html>"></iframe>
<p id="p1">text <img id="p1_img", src="../moz.png"> text</p>
<p id="p2">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p>
</body>
</html>