Bug 975065 - implement Text accessible text range methods, r=tbsaunde
authorAlexander Surkov <surkov.alexander@gmail.com>
Wed, 16 Apr 2014 08:50:28 -0400
changeset 197307 28765a60c8be04738b2de90e074f1e8e9821bc47
parent 197306 b1de460b83dcb73399d7a400d67203f4bd5a59a0
child 197308 43fc4f8b6d495671dec6c67b0740ee1453d31a7e
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstbsaunde
bugs975065
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 975065 - implement Text accessible text range methods, r=tbsaunde
accessible/src/base/TextRange.cpp
accessible/src/base/TextRange.h
accessible/src/generic/HyperTextAccessible.cpp
accessible/src/xpcom/xpcAccessibleTextRange.cpp
accessible/src/xpcom/xpcAccessibleTextRange.h
accessible/tests/mochitest/moz.build
accessible/tests/mochitest/text.js
accessible/tests/mochitest/textrange/a11y.ini
accessible/tests/mochitest/textrange/test_general.html
--- a/accessible/src/base/TextRange.cpp
+++ b/accessible/src/base/TextRange.cpp
@@ -18,8 +18,20 @@ TextRange::TextRange(HyperTextAccessible
 {
 }
 
 void
 TextRange::Text(nsAString& aText) const
 {
 
 }
+
+void
+TextRange::Set(HyperTextAccessible* aRoot,
+               Accessible* aStartContainer, int32_t aStartOffset,
+               Accessible* aEndContainer, int32_t aEndOffset)
+{
+  mRoot = aRoot;
+  mStartContainer = aStartContainer;
+  mEndContainer = aEndContainer;
+  mStartOffset = aStartOffset;
+  mEndOffset = aEndOffset;
+}
--- a/accessible/src/base/TextRange.h
+++ b/accessible/src/base/TextRange.h
@@ -26,38 +26,53 @@ public:
             Accessible* aStartContainer, int32_t aStartOffset,
             Accessible* aEndContainer, int32_t aEndOffset);
   TextRange() {}
   TextRange(TextRange&& aRange) :
     mRoot(Move(aRange.mRoot)), mStartContainer(Move(aRange.mStartContainer)),
     mEndContainer(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);
+    mStartOffset = aRange.mStartOffset;
+    mEndOffset = aRange.mEndOffset;
+    return *this;
+  }
+
   Accessible* StartContainer() const { return mStartContainer; }
   int32_t StartOffset() const { return mStartOffset; }
   Accessible* EndContainer() const { return mEndContainer; }
   int32_t EndOffset() const { return mEndOffset; }
 
   /**
    * Return text enclosed by the range.
    */
   void Text(nsAString& aText) const;
 
   /**
    * Return true if this TextRange object represents an actual range of text.
    */
   bool IsValid() const { return mRoot; }
 
 private:
+  TextRange(const TextRange& aRange) MOZ_DELETE;
+  TextRange& operator=(const TextRange& aRange) MOZ_DELETE;
+
   friend class HyperTextAccessible;
+  friend class xpcAccessibleTextRange;
 
-  TextRange(const TextRange&) MOZ_DELETE;
-  TextRange& operator=(const TextRange&) MOZ_DELETE;
+  void Set(HyperTextAccessible* aRoot,
+           Accessible* aStartContainer, int32_t aStartOffset,
+           Accessible* aEndContainer, int32_t aEndOffset);
 
-  const nsRefPtr<HyperTextAccessible> mRoot;
+  nsRefPtr<HyperTextAccessible> mRoot;
   nsRefPtr<Accessible> mStartContainer;
   nsRefPtr<Accessible> mEndContainer;
   int32_t mStartOffset;
   int32_t mEndOffset;
 };
 
 
 } // namespace a11y
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -1503,38 +1503,76 @@ HyperTextAccessible::ScrollSubstringToPo
     }
     frame = parentFrame;
   }
 }
 
 void
 HyperTextAccessible::EnclosingRange(a11y::TextRange& aRange) const
 {
+  if (IsTextField()) {
+    aRange.Set(mDoc, const_cast<HyperTextAccessible*>(this), 0,
+               const_cast<HyperTextAccessible*>(this), ChildCount());
+  } else {
+    aRange.Set(mDoc, mDoc, 0, mDoc, mDoc->ChildCount());
+  }
 }
 
 void
 HyperTextAccessible::SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const
 {
+  NS_ASSERTION(aRanges->Length() != 0, "TextRange array supposed to be empty");
+
+  Selection* sel = DOMSelection();
+  if (!sel)
+    return;
+
+  aRanges->SetCapacity(sel->RangeCount());
+
+  for (uint32_t idx = 0; idx < sel->RangeCount(); idx++) {
+    nsRange* DOMRange = sel->GetRangeAt(idx);
+    HyperTextAccessible* startParent =
+      nsAccUtils::GetTextContainer(DOMRange->GetStartParent());
+    HyperTextAccessible* endParent =
+      nsAccUtils::GetTextContainer(DOMRange->GetEndParent());
+    if (!startParent || !endParent)
+      continue;
+
+    int32_t startOffset =
+      startParent->DOMPointToOffset(DOMRange->GetStartParent(),
+                                    DOMRange->StartOffset(), false);
+    int32_t endOffset =
+      endParent->DOMPointToOffset(DOMRange->GetEndParent(),
+                                  DOMRange->EndOffset(), true);
+
+    TextRange tr(IsTextField() ? const_cast<HyperTextAccessible*>(this) : mDoc,
+                    startParent, startOffset, endParent, endOffset);
+    *(aRanges->AppendElement()) = Move(tr);
+  }
 }
 
 void
 HyperTextAccessible::VisibleRanges(nsTArray<a11y::TextRange>* aRanges) const
 {
 }
 
 void
 HyperTextAccessible::RangeByChild(Accessible* aChild,
                                   a11y::TextRange& aRange) const
 {
+  aRange.Set(mDoc, aChild, 0, aChild, aChild->ChildCount());
 }
 
 void
 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());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible public
 
 // Accessible protected
 ENameValueFlag
 HyperTextAccessible::NativeName(nsString& aName)
--- a/accessible/src/xpcom/xpcAccessibleTextRange.cpp
+++ b/accessible/src/xpcom/xpcAccessibleTextRange.cpp
@@ -7,42 +7,54 @@
 #include "xpcAccessibleTextRange.h"
 
 #include "HyperTextAccessible.h"
 #include "TextRange.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
-// nsISupports
-NS_IMPL_ISUPPORTS1(xpcAccessibleTextRange, nsIAccessibleTextRange)
+// nsISupports and cycle collection
+
+NS_IMPL_CYCLE_COLLECTION_3(xpcAccessibleTextRange,
+                           mRange.mRoot,
+                           mRange.mStartContainer,
+                           mRange.mEndContainer)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(xpcAccessibleTextRange)
+  NS_INTERFACE_MAP_ENTRY(nsIAccessibleTextRange)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleTextRange)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(xpcAccessibleTextRange)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(xpcAccessibleTextRange)
 
 // nsIAccessibleTextRange
 
 NS_IMETHODIMP
 xpcAccessibleTextRange::GetStartContainer(nsIAccessible** aAnchor)
 {
   NS_ENSURE_ARG_POINTER(aAnchor);
-  *aAnchor = static_cast<nsIAccessible*>(mRange.StartContainer());
+  NS_IF_ADDREF(*aAnchor = static_cast<nsIAccessible*>(mRange.StartContainer()));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleTextRange::GetStartOffset(int32_t* aOffset)
 {
   NS_ENSURE_ARG_POINTER(aOffset);
   *aOffset = mRange.StartOffset();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleTextRange::GetEndContainer(nsIAccessible** aAnchor)
 {
   NS_ENSURE_ARG_POINTER(aAnchor);
-  *aAnchor = static_cast<nsIAccessible*>(mRange.EndContainer());
+  NS_IF_ADDREF(*aAnchor = static_cast<nsIAccessible*>(mRange.EndContainer()));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleTextRange::GetEndOffset(int32_t* aOffset)
 {
   NS_ENSURE_ARG_POINTER(aOffset);
   *aOffset = mRange.EndOffset();
--- a/accessible/src/xpcom/xpcAccessibleTextRange.h
+++ b/accessible/src/xpcom/xpcAccessibleTextRange.h
@@ -6,26 +6,28 @@
 
 #ifndef mozilla_a11y_xpcAccessibleTextRange_h_
 #define mozilla_a11y_xpcAccessibleTextRange_h_
 
 #include "nsIAccessibleTextRange.h"
 #include "TextRange.h"
 
 #include "mozilla/Move.h"
+#include "nsCycleCollectionParticipant.h"
 
 namespace mozilla {
 namespace a11y {
 
 class TextRange;
 
 class xpcAccessibleTextRange MOZ_FINAL : public nsIAccessibleTextRange
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(xpcAccessibleTextRange)
 
   NS_IMETHOD GetStartContainer(nsIAccessible** 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 GetEndOffset(int32_t* aOffset) MOZ_FINAL MOZ_OVERRIDE;
   NS_IMETHOD GetText(nsAString& aText) MOZ_FINAL MOZ_OVERRIDE;
 
 private:
--- a/accessible/tests/mochitest/moz.build
+++ b/accessible/tests/mochitest/moz.build
@@ -29,10 +29,11 @@ DIRS += [
     'textselection',
     'treeupdate',
     'value',
 ]
 
 A11Y_MANIFESTS += [
     'a11y.ini',
     'events/a11y.ini',
+    'textrange/a11y.ini',
     'tree/a11y.ini',
 ]
--- a/accessible/tests/mochitest/text.js
+++ b/accessible/tests/mochitest/text.js
@@ -452,16 +452,29 @@ function testTextGetSelection(aID, aStar
   acc.getSelectionBounds(aSelectionIndex, startObj, endObj);
 
   is(startObj.value, aStartOffset, text + ": wrong start offset for index '" +
      aSelectionIndex + "'");
   is(endObj.value, aEndOffset, text + ": wrong end offset for index '" +
      aSelectionIndex + "'");
 }
 
+function testTextRange(aRange, aStartContainer, aStartOffset,
+                       aEndContainer, aEndOffset)
+{
+  is(aRange.startContainer, getAccessible(aStartContainer),
+     "Wrong start container");
+  is(aRange.startOffset, aStartOffset,
+     "Wrong start offset");
+  is(aRange.endContainer, getAccessible(aEndContainer),
+     "Wrong end container");
+  is(aRange.endOffset, aEndOffset,
+     "Wrong end offset");
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Private
 
 function testTextSuperHelper(aFuncName, aArgs)
 {
   // List of tests.
   if (aArgs[2] instanceof Array) {
     var ids = (aArgs[0] instanceof Array) ? aArgs[0] : [ aArgs[0] ];
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/textrange/a11y.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[test_general.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/textrange/test_general.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Text Range 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">
+
+    function doTest()
+    {
+      var input = getAccessible("input", [ nsIAccessibleText ]);
+      testTextRange(input.enclosingRange, input, 0, input, 1);
+
+      var ta = getAccessible("textarea", [ nsIAccessibleText ]);
+      testTextRange(ta.enclosingRange, ta, 0, ta, 1);
+
+      var iframeDoc = getAccessible(getNode("iframe").contentDocument,
+                                    [ nsIAccessibleText ]);
+      testTextRange(iframeDoc.enclosingRange, iframeDoc, 0, iframeDoc, 1);
+
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     title="Implement Text accessible text range methods"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=975065">Bug 975065</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <input id="input" value="hello">
+  <textarea id="textarea">hello</textarea>
+  <iframe id="iframe" src="data:text/html,<p>hello</p>"></iframe>
+
+</body>
+</html>