new file mode 100644
--- /dev/null
+++ b/accessible/base/TextRange-inl.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_a11y_TextRange_inl_h__
+#define mozilla_a11y_TextRange_inl_h__
+
+#include "TextRange.h"
+#include "HyperTextAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+inline Accessible*
+TextRange::Container() const
+{
+ uint32_t pos1 = 0, pos2 = 0;
+ nsAutoTArray<Accessible*, 30> parents1, parents2;
+ return CommonParent(mStartContainer, mEndContainer,
+ &parents1, &pos1, &parents2, &pos2);
+}
+
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
--- a/accessible/base/TextRange.cpp
+++ b/accessible/base/TextRange.cpp
@@ -1,18 +1,17 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "TextRange.h"
+#include "TextRange-inl.h"
#include "Accessible-inl.h"
-#include "HyperTextAccessible.h"
#include "nsAccUtils.h"
namespace mozilla {
namespace a11y {
////////////////////////////////////////////////////////////////////////////////
// TextPoint
@@ -54,90 +53,37 @@ TextPoint::operator <(const TextPoint& a
TextRange::TextRange(HyperTextAccessible* aRoot,
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);
+
+ uint32_t pos1 = 0, pos2 = 0;
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;
- }
+ Accessible* container =
+ CommonParent(p1, p2, &parents1, &pos1, &parents2, &pos2);
// 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);
@@ -191,16 +137,105 @@ TextRange::Bounds(nsTArray<nsIntRect> aR
}
void
TextRange::Normalize(ETextUnit aUnit)
{
}
+bool
+TextRange::Crop(Accessible* aContainer)
+{
+ uint32_t boundaryPos = 0, containerPos = 0;
+ nsAutoTArray<Accessible*, 30> boundaryParents, containerParents;
+
+ // Crop the start boundary.
+ Accessible* container = nullptr;
+ Accessible* boundary = mStartContainer->GetChildAtOffset(mStartOffset);
+ if (boundary != aContainer) {
+ CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
+ &containerParents, &containerPos);
+
+ if (boundaryPos == 0) {
+ if (containerPos != 0) {
+ // The container is contained by the start boundary, reduce the range to
+ // the point starting at the container.
+ aContainer->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset);
+ static_cast<Accessible*>(mStartContainer)->AddRef();
+ }
+ else {
+ // The start boundary and the container are siblings.
+ container = aContainer;
+ }
+ }
+ else if (containerPos != 0) {
+ // The container does not contain the start boundary.
+ boundary = boundaryParents[boundaryPos];
+ container = containerParents[containerPos];
+ }
+
+ if (container) {
+ // If the range start is after the container, then make the range invalid.
+ if (boundary->IndexInParent() > container->IndexInParent()) {
+ return !!(mRoot = nullptr);
+ }
+
+ // If the range starts before the container, then reduce the range to
+ // the point starting at the container.
+ if (boundary->IndexInParent() < container->IndexInParent()) {
+ container->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset);
+ mStartContainer.get()->AddRef();
+ }
+ }
+
+ boundaryParents.SetLengthAndRetainStorage(0);
+ containerParents.SetLengthAndRetainStorage(0);
+ }
+
+ boundary = mEndContainer->GetChildAtOffset(mEndOffset);
+ if (boundary == aContainer) {
+ return true;
+ }
+
+ // Crop the end boundary.
+ container = nullptr;
+ CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
+ &containerParents, &containerPos);
+
+ if (boundaryPos == 0) {
+ if (containerPos != 0) {
+ aContainer->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
+ static_cast<Accessible*>(mEndContainer)->AddRef();
+ }
+ else {
+ container = aContainer;
+ }
+ }
+ else if (containerPos != 0) {
+ boundary = boundaryParents[boundaryPos];
+ container = containerParents[containerPos];
+ }
+
+ if (!container) {
+ return true;
+ }
+
+ if (boundary->IndexInParent() < container->IndexInParent()) {
+ return !!(mRoot = nullptr);
+ }
+
+ if (boundary->IndexInParent() > container->IndexInParent()) {
+ container->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
+ static_cast<Accessible*>(mEndContainer)->AddRef();
+ }
+
+ return true;
+}
+
void
TextRange::FindText(const nsAString& aText, EDirection aDirection,
nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const
{
}
void
@@ -291,10 +326,51 @@ TextRange::TextInternal(nsAString& aText
void
TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount,
HyperTextAccessible& aContainer, int32_t aOffset,
HyperTextAccessible* aStopContainer, int32_t aStopOffset)
{
}
+Accessible*
+TextRange::CommonParent(Accessible* aAcc1, Accessible* aAcc2,
+ nsTArray<Accessible*>* aParents1, uint32_t* aPos1,
+ nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const
+{
+ if (aAcc1 == aAcc2) {
+ return aAcc1;
+ }
+
+ MOZ_ASSERT(aParents1->Length() == 0 || aParents2->Length() == 0,
+ "Wrong arguments");
+
+ // Build the chain of parents.
+ Accessible* p1 = aAcc1;
+ Accessible* p2 = aAcc2;
+ do {
+ aParents1->AppendElement(p1);
+ p1 = p1->Parent();
+ } while (p1);
+ do {
+ aParents2->AppendElement(p2);
+ p2 = p2->Parent();
+ } while (p2);
+
+ // Find where the parent chain differs
+ *aPos1 = aParents1->Length();
+ *aPos2 = aParents2->Length();
+ Accessible* parent = nullptr;
+ uint32_t len = 0;
+ for (len = std::min(*aPos1, *aPos2); len > 0; --len) {
+ Accessible* child1 = aParents1->ElementAt(--(*aPos1));
+ Accessible* child2 = aParents2->ElementAt(--(*aPos2));
+ if (child1 != child2)
+ break;
+
+ parent = child1;
+ }
+
+ return parent;
+}
+
} // namespace a11y
} // namespace mozilla
--- a/accessible/base/TextRange.h
+++ b/accessible/base/TextRange.h
@@ -126,16 +126,22 @@ public:
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);
+ /**
+ * Crops the range if it overlaps the given accessible element boundaries,
+ * returns true if the range was cropped successfully.
+ */
+ bool Crop(Accessible* aContainer);
+
enum EDirection {
eBackward,
eForward
};
/**
* Return range enclosing the found text.
*/
@@ -238,16 +244,24 @@ private:
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);
+ /**
+ * A helper method returning a common parent for two given accessible
+ * elements.
+ */
+ Accessible* CommonParent(Accessible* aAcc1, Accessible* aAcc2,
+ nsTArray<Accessible*>* aParents1, uint32_t* aPos1,
+ nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const;
+
RefPtr<HyperTextAccessible> mRoot;
RefPtr<HyperTextAccessible> mStartContainer;
RefPtr<HyperTextAccessible> mEndContainer;
int32_t mStartOffset;
int32_t mEndOffset;
};
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -2245,16 +2245,40 @@ Accessible::AnchorAt(uint32_t aAnchorInd
already_AddRefed<nsIURI>
Accessible::AnchorURIAt(uint32_t aAnchorIndex)
{
NS_PRECONDITION(IsLink(), "AnchorURIAt is called on not hyper link!");
return nullptr;
}
+void
+Accessible::ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset,
+ bool aIsBefore) const
+{
+ if (IsHyperText()) {
+ *aContainer = const_cast<Accessible*>(this)->AsHyperText();
+ *aOffset = aIsBefore ? 0 : (*aContainer)->CharacterCount();
+ return;
+ }
+
+ const Accessible* child = nullptr;
+ const Accessible* parent = this;
+ do {
+ child = parent;
+ parent = parent->Parent();
+ } while (parent && !parent->IsHyperText());
+
+ if (parent) {
+ *aContainer = const_cast<Accessible*>(parent)->AsHyperText();
+ *aOffset = (*aContainer)->GetChildOffset(
+ child->IndexInParent() + static_cast<int32_t>(!aIsBefore));
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// SelectAccessible
void
Accessible::SelectedItems(nsTArray<Accessible*>* aItems)
{
AccIterator iter(this, filters::GetSelected);
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -753,16 +753,22 @@ public:
*/
virtual Accessible* AnchorAt(uint32_t aAnchorIndex);
/**
* Returns an anchor URI at the given index.
*/
virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex);
+ /**
+ * Returns a text point for the accessible element.
+ */
+ void ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset,
+ bool aIsBefore = true) const;
+
//////////////////////////////////////////////////////////////////////////////
// SelectAccessible
/**
* Return an array of selected items.
*/
virtual void SelectedItems(nsTArray<Accessible*>* aItems);
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1764,17 +1764,17 @@ HyperTextAccessible::EnclosingRange(a11y
} else {
aRange.Set(mDoc, mDoc, 0, mDoc, mDoc->CharacterCount());
}
}
void
HyperTextAccessible::SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const
{
- NS_ASSERTION(aRanges->Length() != 0, "TextRange array supposed to be empty");
+ MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty");
dom::Selection* sel = DOMSelection();
if (!sel)
return;
aRanges->SetCapacity(sel->RangeCount());
for (uint32_t idx = 0; idx < sel->RangeCount(); idx++) {
--- a/accessible/generic/HyperTextAccessible.h
+++ b/accessible/generic/HyperTextAccessible.h
@@ -253,17 +253,17 @@ public:
/**
* 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
*/
- int32_t GetChildOffset(Accessible* aChild,
+ int32_t GetChildOffset(const Accessible* aChild,
bool aInvalidateAfter = false) const
{
int32_t index = GetIndexOf(aChild);
return index == -1 ? -1 : GetChildOffset(index, aInvalidateAfter);
}
/**
* Return text offset for the child accessible index.
--- a/accessible/interfaces/ia2/Makefile.in
+++ b/accessible/interfaces/ia2/Makefile.in
@@ -6,16 +6,17 @@ IA2DIR = $(topsrcdir)/other-licen
GARBAGE += $(MIDL_GENERATED_FILES)
# Please keep this list in sync with the moz.build file until the rest of this
# Makefile is ported over.
MIDL_INTERFACES = \
Accessible2.idl \
Accessible2_2.idl \
+ Accessible2_3.idl \
AccessibleAction.idl \
AccessibleApplication.idl \
AccessibleComponent.idl \
AccessibleDocument.idl \
AccessibleEditableText.idl \
AccessibleHyperlink.idl \
AccessibleHypertext.idl \
AccessibleHypertext2.idl \
--- a/accessible/interfaces/nsIAccessibleTextRange.idl
+++ b/accessible/interfaces/nsIAccessibleTextRange.idl
@@ -8,17 +8,17 @@
interface nsIAccessible;
interface nsIAccessibleText;
interface nsIArray;
interface nsIVariant;
/**
* A range representing a piece of text in the document.
*/
-[scriptable, uuid(525b3401-8a67-4822-b35d-661065767cd8)]
+[scriptable, uuid(c4515623-55f9-4543-a3d5-c1e9afa588f4)]
interface nsIAccessibleTextRange : nsISupports
{
readonly attribute nsIAccessibleText startContainer;
readonly attribute long startOffset;
readonly attribute nsIAccessibleText endContainer;
readonly attribute long endOffset;
/**
@@ -77,16 +77,21 @@ interface nsIAccessibleTextRange : nsISu
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);
/**
+ * Crops the range by the given accessible element.
+ */
+ boolean crop(in nsIAccessible aContainer);
+
+ /**
* Return range enclosing the found text.
*/
nsIAccessibleTextRange findText(in AString aText, in boolean aIsBackward,
in boolean aIsIgnoreCase);
/**
* Text attributes. Used in conjunction with findAttrs().
*/
--- a/accessible/tests/mochitest/text.js
+++ b/accessible/tests/mochitest/text.js
@@ -468,16 +468,20 @@ function testTextRange(aRange, aRangeDes
"Wrong start container of " + aRangeDescr);
is(aRange.startOffset, aStartOffset,
"Wrong start offset of " + aRangeDescr);
isObject(aRange.endContainer, getAccessible(aEndContainer),
"Wrong end container of " + aRangeDescr);
is(aRange.endOffset, aEndOffset,
"Wrong end offset of " + aRangeDescr);
+ if (aText === undefined) {
+ return;
+ }
+
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);
--- a/accessible/tests/mochitest/textrange/a11y.ini
+++ b/accessible/tests/mochitest/textrange/a11y.ini
@@ -1,3 +1,4 @@
[DEFAULT]
[test_general.html]
+[test_selection.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/textrange/test_selection.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Text Range selection tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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()
+ {
+ var sel = window.getSelection();
+ var p = getNode("p1");
+ var a = getNode("p2_a");
+
+ var range = document.createRange();
+ sel.addRange(range);
+
+ // the accessible is contained by the range
+ range.selectNode(p);
+
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #1", document, 3, document, 4);
+
+ ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #1.");
+ testTextRange(a11yrange, "cropped range #1", a, 0, a, 5);
+
+ // the range is contained by the accessible
+ range.selectNode(a);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #2", p, 5, p, 6);
+
+ ok(a11yrange.crop(getAccessible(p)), "Range failed to crop #2.");
+ testTextRange(a11yrange, "cropped range #2", p, 5, p, 6);
+
+ // the range starts before the accessible and ends inside it
+ range.setStart(p, 0);
+ range.setEndAfter(a.firstChild, 4);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #3", p, 0, a, 4);
+
+ ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #3.");
+ testTextRange(a11yrange, "cropped range #3", a, 0, a, 4);
+
+ // the range starts inside the accessible and ends after it
+ range.setStart(a.firstChild, 1);
+ range.setEndAfter(p);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #4", a, 1, document, 4);
+
+ ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #4.");
+ testTextRange(a11yrange, "cropped range #4", a, 1, a, 5);
+
+ // the range ends before the accessible
+ range.setStart(p.firstChild, 0);
+ range.setEnd(p.firstChild, 4);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #5", p, 0, p, 4);
+ ok(!a11yrange.crop(getAccessible(a)), "Crop #5 succeeded while it shouldn't");
+
+ // the range starts after the accessible
+ range.setStart(p.lastChild, 0);
+ range.setEnd(p.lastChild, 4);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #6", p, 6, p, 10);
+
+ ok(!a11yrange.crop(getAccessible(a)), "Crop #6 succeeded while it shouldn't");
+
+ // crop a range by a table
+ range.selectNode(getNode("c2"));
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #7", document, 4, document, 5);
+
+ ok(a11yrange.crop(getAccessible("table")), "Range failed to crop #7.");
+ testTextRange(a11yrange, "cropped range #7", "c2", 5, "c2", 6);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Implement IAccessible2_3::selectionRanges"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1233118">Bug 1233118</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="p1">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p>
+
+ <div id="c2">start<table id="table"><tr><td>cell</td></tr></table>end</div>
+</body>
+</html>
--- a/accessible/windows/ia2/ia2Accessible.cpp
+++ b/accessible/windows/ia2/ia2Accessible.cpp
@@ -3,26 +3,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccessibleWrap.h"
#include "Accessible2_i.c"
#include "Accessible2_2_i.c"
+#include "Accessible2_3_i.c"
#include "AccessibleRole.h"
#include "AccessibleStates.h"
#include "Compatibility.h"
#include "ia2AccessibleRelation.h"
#include "IUnknownImpl.h"
#include "nsCoreUtils.h"
#include "nsIAccessibleTypes.h"
#include "mozilla/a11y/PDocAccessible.h"
#include "Relation.h"
+#include "TextRange-inl.h"
#include "nsAccessibilityService.h"
#include "nsIPersistentProperties2.h"
#include "nsISimpleEnumerator.h"
using namespace mozilla;
using namespace mozilla::a11y;
@@ -35,17 +37,19 @@ template<typename String> static void Es
STDMETHODIMP
ia2Accessible::QueryInterface(REFIID iid, void** ppv)
{
if (!ppv)
return E_INVALIDARG;
*ppv = nullptr;
- if (IID_IAccessible2_2 == iid)
+ if (IID_IAccessible2_3 == iid)
+ *ppv = static_cast<IAccessible2_3*>(this);
+ else if (IID_IAccessible2_2 == iid)
*ppv = static_cast<IAccessible2_2*>(this);
else if (IID_IAccessible2 == iid && !Compatibility::IsIA2Off())
*ppv = static_cast<IAccessible2*>(this);
if (*ppv) {
(reinterpret_cast<IUnknown*>(*ppv))->AddRef();
return S_OK;
}
@@ -744,16 +748,68 @@ ia2Accessible::get_relationTargetsOfType
(*aTargets)[i]->AddRef();
}
return S_OK;
A11Y_TRYBLOCK_END
}
+STDMETHODIMP
+ia2Accessible::get_selectionRanges(IA2Range** aRanges,
+ long *aNRanges)
+{
+ A11Y_TRYBLOCK_BEGIN
+
+ if (!aRanges || !aNRanges || aNRanges <= 0)
+ return E_INVALIDARG;
+
+ *aNRanges = 0;
+
+ AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
+ if (acc->IsDefunct())
+ return CO_E_OBJNOTCONNECTED;
+
+ nsAutoTArray<TextRange, 1> ranges;
+ acc->Document()->SelectionRanges(&ranges);
+ uint32_t len = ranges.Length();
+ for (uint32_t idx = 0; idx < len; idx++) {
+ if (!ranges[idx].Crop(acc)) {
+ ranges.RemoveElementAt(idx);
+ }
+ }
+
+ *aNRanges = ranges.Length();
+ *aRanges = static_cast<IA2Range*>(
+ ::CoTaskMemAlloc(sizeof(IA2Range) * *aNRanges));
+ if (!*aRanges)
+ return E_OUTOFMEMORY;
+
+ for (uint32_t idx = 0; idx < static_cast<uint32_t>(*aNRanges); idx++) {
+ AccessibleWrap* anchor =
+ static_cast<AccessibleWrap*>(ranges[idx].StartContainer());
+ (*aRanges)[idx].anchor = static_cast<IAccessible2*>(anchor);
+ anchor->AddRef();
+
+ (*aRanges)[idx].anchorOffset = ranges[idx].StartOffset();
+
+ AccessibleWrap* active =
+ static_cast<AccessibleWrap*>(ranges[idx].EndContainer());
+ (*aRanges)[idx].active = static_cast<IAccessible2*>(active);
+ active->AddRef();
+
+ (*aRanges)[idx].activeOffset = ranges[idx].EndOffset();
+ }
+
+ return S_OK;
+
+ A11Y_TRYBLOCK_END
+}
+
+
////////////////////////////////////////////////////////////////////////////////
// Helpers
template<typename String>
static inline void
EscapeAttributeChars(String& aStr)
{
int32_t offset = 0;
--- a/accessible/windows/ia2/ia2Accessible.h
+++ b/accessible/windows/ia2/ia2Accessible.h
@@ -4,23 +4,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_ia2Accessible_h_
#define mozilla_a11y_ia2Accessible_h_
#include "nsISupports.h"
-#include "Accessible2_2.h"
+#include "Accessible2_3.h"
namespace mozilla {
namespace a11y {
class Attribute;
-class ia2Accessible : public IAccessible2_2
+class ia2Accessible : public IAccessible2_3
{
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID, void**);
// IAccessible2
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nRelations(
@@ -99,16 +99,21 @@ public:
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relationTargetsOfType(
/* [in] */ BSTR type,
/* [in] */ long maxTargets,
/* [out, size_is(,*nTargets)] */ IUnknown*** targets,
/* [out, retval] */ long* nTargets
);
+ // IAccessible2_3
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectionRanges(
+ /* [out, size_is(,*nRanges)] */ IA2Range** ranges,
+ /* [out, retval] */ long *nRanges);
+
// Helper method
static HRESULT ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
BSTR* aIA2Attributes);
static HRESULT ConvertToIA2Attributes(nsTArray<Attribute>* aAttributes,
BSTR* aIA2Attributes);
};
} // namespace a11y
--- a/accessible/xpcom/xpcAccessibleTextRange.cpp
+++ b/accessible/xpcom/xpcAccessibleTextRange.cpp
@@ -1,17 +1,17 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "xpcAccessibleTextRange.h"
-#include "TextRange.h"
+#include "TextRange-inl.h"
#include "xpcAccessibleDocument.h"
#include "nsIMutableArray.h"
#include "nsComponentManagerUtils.h"
#include "nsQueryObject.h"
using namespace mozilla;
using namespace mozilla::a11y;
@@ -166,16 +166,26 @@ xpcAccessibleTextRange::MoveEnd(uint32_t
NS_IMETHODIMP
xpcAccessibleTextRange::Normalize(uint32_t aUnit)
{
return NS_OK;
}
NS_IMETHODIMP
+xpcAccessibleTextRange::Crop(nsIAccessible* aContainer, bool* aSuccess)
+{
+ Accessible* container = aContainer->ToInternalAccessible();
+ NS_ENSURE_TRUE(container, NS_ERROR_INVALID_ARG);
+
+ *aSuccess = mRange.Crop(container);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
xpcAccessibleTextRange::FindText(const nsAString& aText, bool aIsBackward,
bool aIsIgnoreCase,
nsIAccessibleTextRange** aRange)
{
return NS_OK;
}
NS_IMETHODIMP
--- a/accessible/xpcom/xpcAccessibleTextRange.h
+++ b/accessible/xpcom/xpcAccessibleTextRange.h
@@ -44,16 +44,17 @@ public:
uint32_t aOtherRangeEndPoint,
int32_t* aResult) final override;
NS_IMETHOD GetText(nsAString& aText) final override;
NS_IMETHOD GetBounds(nsIArray** aRectList) final override;
NS_IMETHOD Move(uint32_t aUnit, int32_t aCount) final override;
NS_IMETHOD MoveStart(uint32_t aUnit, int32_t aCount) final override;
NS_IMETHOD MoveEnd(uint32_t aUnit, int32_t aCount) final override;
NS_IMETHOD Normalize(uint32_t aUnit) final override;
+ NS_IMETHOD Crop(nsIAccessible* aContainer, bool* aSuccess) final override;
NS_IMETHOD FindText(const nsAString& aText, bool aIsBackward, bool aIsIgnoreCase,
nsIAccessibleTextRange** aRange) final override;
NS_IMETHOD FindAttr(uint32_t aAttr, nsIVariant* aVal, bool aIsBackward,
nsIAccessibleTextRange** aRange) final override;
NS_IMETHOD AddToSelection() final override;
NS_IMETHOD RemoveFromSelection() final override;
NS_IMETHOD Select() final override;
NS_IMETHOD ScrollIntoView(uint32_t aHow) final override;
new file mode 100644
--- /dev/null
+++ b/other-licenses/ia2/Accessible2_3.idl
@@ -0,0 +1,47 @@
+import "objidl.idl";
+import "oaidl.idl";
+import "oleacc.idl";
+import "Accessible2_2.idl";
+
+/**
+ * This structure represents a directional range of the content. It is defined
+ * by two points in the content, where each one is defined by an accessible
+ * object and an offset relative to it. A typical case of a range point is
+ * a text accessible and text offset within it.
+ *
+ * The "anchor" is one point of the range and typically remains constant.
+ * The other point is the "active" point, which typically corresponds to
+ * the user's focus or point of interest. The user moves the active point to
+ * expand or collapse the range. In most cases, anchor is the start of the range
+ * and active is the end. However, in case of selection, when selecting
+ * backwards (e.g. pressing shift+left arrow in a text field), the start of
+ * the range is the active point, as the user moves this to manipulate
+ * the selection.
+ */
+typedef struct IA2Range {
+ IUnknown* anchor;
+ long anchorOffset;
+ IUnknown* active;
+ long activeOffset;
+} IA2Range;
+
+/**
+ * @brief This interface is an extension of IAccessible2_2 and IAccessible2
+ * interfaces.
+ */
+[object, uuid(5BE18059-762E-4E73-9476-ABA294FED411)]
+interface IAccessible2_3 : IAccessible2_2
+{
+ /**
+ * @brief Returns an array of ranges for selections within the accessible.
+ * @param [out] the array of selection ranges
+ * @param [out] the array length
+ * @retval S_OK
+ * @retval S_FALSE returned if there is no selection within the accessible
+ */
+ [propget] HRESULT selectionRanges
+ (
+ [out, size_is(,*nRanges)] IA2Range **ranges,
+ [out, retval] long *nRanges
+ );
+}
--- a/other-licenses/ia2/IA2TypeLibrary.idl
+++ b/other-licenses/ia2/IA2TypeLibrary.idl
@@ -66,16 +66,17 @@ cpp_quote("")
]
library IAccessible2Lib
{
importlib ("stdole2.tlb");
importlib ("oleacc.dll");
interface IAccessible2;
interface IAccessible2_2;
+ interface IAccessible2_3;
interface IAccessibleAction;
interface IAccessibleApplication;
interface IAccessibleComponent;
interface IAccessibleDocument;
interface IAccessibleEditableText;
interface IAccessibleHyperlink;
interface IAccessibleHypertext;
interface IAccessibleHypertext2;