LCOV - code coverage report
Current view: top level - accessible/generic - HyperTextAccessible.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1144 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 56 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 sw=2 et tw=78: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "HyperTextAccessible-inl.h"
       8             : 
       9             : #include "Accessible-inl.h"
      10             : #include "nsAccessibilityService.h"
      11             : #include "nsIAccessibleTypes.h"
      12             : #include "DocAccessible.h"
      13             : #include "HTMLListAccessible.h"
      14             : #include "Role.h"
      15             : #include "States.h"
      16             : #include "TextAttrs.h"
      17             : #include "TextRange.h"
      18             : #include "TreeWalker.h"
      19             : 
      20             : #include "nsCaret.h"
      21             : #include "nsContentUtils.h"
      22             : #include "nsFocusManager.h"
      23             : #include "nsIDOMRange.h"
      24             : #include "nsIEditingSession.h"
      25             : #include "nsContainerFrame.h"
      26             : #include "nsFrameSelection.h"
      27             : #include "nsILineIterator.h"
      28             : #include "nsIInterfaceRequestorUtils.h"
      29             : #include "nsIPersistentProperties2.h"
      30             : #include "nsIScrollableFrame.h"
      31             : #include "nsIServiceManager.h"
      32             : #include "nsITextControlElement.h"
      33             : #include "nsIMathMLFrame.h"
      34             : #include "nsTextFragment.h"
      35             : #include "mozilla/BinarySearch.h"
      36             : #include "mozilla/dom/Element.h"
      37             : #include "mozilla/EventStates.h"
      38             : #include "mozilla/dom/Selection.h"
      39             : #include "mozilla/MathAlgorithms.h"
      40             : #include "gfxSkipChars.h"
      41             : #include <algorithm>
      42             : 
      43             : using namespace mozilla;
      44             : using namespace mozilla::a11y;
      45             : 
      46             : ////////////////////////////////////////////////////////////////////////////////
      47             : // HyperTextAccessible
      48             : ////////////////////////////////////////////////////////////////////////////////
      49             : 
      50           0 : HyperTextAccessible::
      51           0 :   HyperTextAccessible(nsIContent* aNode, DocAccessible* aDoc) :
      52           0 :   AccessibleWrap(aNode, aDoc)
      53             : {
      54           0 :   mType = eHyperTextType;
      55           0 :   mGenericTypes |= eHyperText;
      56           0 : }
      57             : 
      58           0 : NS_IMPL_ISUPPORTS_INHERITED0(HyperTextAccessible, Accessible)
      59             : 
      60             : role
      61           0 : HyperTextAccessible::NativeRole()
      62             : {
      63           0 :   a11y::role r = GetAccService()->MarkupRole(mContent);
      64           0 :   if (r != roles::NOTHING)
      65           0 :     return r;
      66             : 
      67           0 :   nsIFrame* frame = GetFrame();
      68           0 :   if (frame && frame->IsInlineFrame())
      69           0 :     return roles::TEXT;
      70             : 
      71           0 :   return roles::TEXT_CONTAINER;
      72             : }
      73             : 
      74             : uint64_t
      75           0 : HyperTextAccessible::NativeState()
      76             : {
      77           0 :   uint64_t states = AccessibleWrap::NativeState();
      78             : 
      79           0 :   if (mContent->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
      80           0 :     states |= states::EDITABLE;
      81             : 
      82           0 :   } else if (mContent->IsHTMLElement(nsGkAtoms::article)) {
      83             :     // We want <article> to behave like a document in terms of readonly state.
      84           0 :     states |= states::READONLY;
      85             :   }
      86             : 
      87           0 :   if (HasChildren())
      88           0 :     states |= states::SELECTABLE_TEXT;
      89             : 
      90           0 :   return states;
      91             : }
      92             : 
      93             : nsIntRect
      94           0 : HyperTextAccessible::GetBoundsInFrame(nsIFrame* aFrame,
      95             :                                       uint32_t aStartRenderedOffset,
      96             :                                       uint32_t aEndRenderedOffset)
      97             : {
      98           0 :   nsPresContext* presContext = mDoc->PresContext();
      99           0 :   if (!aFrame->IsTextFrame()) {
     100           0 :     return aFrame->GetScreenRectInAppUnits().
     101           0 :       ToNearestPixels(presContext->AppUnitsPerDevPixel());
     102             :   }
     103             : 
     104             :   // Substring must be entirely within the same text node.
     105             :   int32_t startContentOffset, endContentOffset;
     106           0 :   nsresult rv = RenderedToContentOffset(aFrame, aStartRenderedOffset, &startContentOffset);
     107           0 :   NS_ENSURE_SUCCESS(rv, nsIntRect());
     108           0 :   rv = RenderedToContentOffset(aFrame, aEndRenderedOffset, &endContentOffset);
     109           0 :   NS_ENSURE_SUCCESS(rv, nsIntRect());
     110             : 
     111             :   nsIFrame *frame;
     112             :   int32_t startContentOffsetInFrame;
     113             :   // Get the right frame continuation -- not really a child, but a sibling of
     114             :   // the primary frame passed in
     115           0 :   rv = aFrame->GetChildFrameContainingOffset(startContentOffset, false,
     116           0 :                                              &startContentOffsetInFrame, &frame);
     117           0 :   NS_ENSURE_SUCCESS(rv, nsIntRect());
     118             : 
     119           0 :   nsRect screenRect;
     120           0 :   while (frame && startContentOffset < endContentOffset) {
     121             :     // Start with this frame's screen rect, which we will shrink based on
     122             :     // the substring we care about within it. We will then add that frame to
     123             :     // the total screenRect we are returning.
     124           0 :     nsRect frameScreenRect = frame->GetScreenRectInAppUnits();
     125             : 
     126             :     // Get the length of the substring in this frame that we want the bounds for
     127             :     int32_t startFrameTextOffset, endFrameTextOffset;
     128           0 :     frame->GetOffsets(startFrameTextOffset, endFrameTextOffset);
     129           0 :     int32_t frameTotalTextLength = endFrameTextOffset - startFrameTextOffset;
     130           0 :     int32_t seekLength = endContentOffset - startContentOffset;
     131           0 :     int32_t frameSubStringLength = std::min(frameTotalTextLength - startContentOffsetInFrame, seekLength);
     132             : 
     133             :     // Add the point where the string starts to the frameScreenRect
     134           0 :     nsPoint frameTextStartPoint;
     135           0 :     rv = frame->GetPointFromOffset(startContentOffset, &frameTextStartPoint);
     136           0 :     NS_ENSURE_SUCCESS(rv, nsIntRect());
     137             : 
     138             :     // Use the point for the end offset to calculate the width
     139           0 :     nsPoint frameTextEndPoint;
     140           0 :     rv = frame->GetPointFromOffset(startContentOffset + frameSubStringLength, &frameTextEndPoint);
     141           0 :     NS_ENSURE_SUCCESS(rv, nsIntRect());
     142             : 
     143           0 :     frameScreenRect.x += std::min(frameTextStartPoint.x, frameTextEndPoint.x);
     144           0 :     frameScreenRect.width = mozilla::Abs(frameTextStartPoint.x - frameTextEndPoint.x);
     145             : 
     146           0 :     screenRect.UnionRect(frameScreenRect, screenRect);
     147             : 
     148             :     // Get ready to loop back for next frame continuation
     149           0 :     startContentOffset += frameSubStringLength;
     150           0 :     startContentOffsetInFrame = 0;
     151           0 :     frame = frame->GetNextContinuation();
     152             :   }
     153             : 
     154           0 :   return screenRect.ToNearestPixels(presContext->AppUnitsPerDevPixel());
     155             : }
     156             : 
     157             : void
     158           0 : HyperTextAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
     159             :                                    nsAString& aText)
     160             : {
     161           0 :   aText.Truncate();
     162             : 
     163           0 :   index_t startOffset = ConvertMagicOffset(aStartOffset);
     164           0 :   index_t endOffset = ConvertMagicOffset(aEndOffset);
     165           0 :   if (!startOffset.IsValid() || !endOffset.IsValid() ||
     166           0 :       startOffset > endOffset || endOffset > CharacterCount()) {
     167           0 :     NS_ERROR("Wrong in offset");
     168           0 :     return;
     169             :   }
     170             : 
     171           0 :   int32_t startChildIdx = GetChildIndexAtOffset(startOffset);
     172           0 :   if (startChildIdx == -1)
     173           0 :     return;
     174             : 
     175           0 :   int32_t endChildIdx = GetChildIndexAtOffset(endOffset);
     176           0 :   if (endChildIdx == -1)
     177           0 :     return;
     178             : 
     179           0 :   if (startChildIdx == endChildIdx) {
     180           0 :     int32_t childOffset =  GetChildOffset(startChildIdx);
     181           0 :     if (childOffset == -1)
     182           0 :       return;
     183             : 
     184           0 :     Accessible* child = GetChildAt(startChildIdx);
     185           0 :     child->AppendTextTo(aText, startOffset - childOffset,
     186           0 :                         endOffset - startOffset);
     187           0 :     return;
     188             :   }
     189             : 
     190           0 :   int32_t startChildOffset =  GetChildOffset(startChildIdx);
     191           0 :   if (startChildOffset == -1)
     192           0 :     return;
     193             : 
     194           0 :   Accessible* startChild = GetChildAt(startChildIdx);
     195           0 :   startChild->AppendTextTo(aText, startOffset - startChildOffset);
     196             : 
     197           0 :   for (int32_t childIdx = startChildIdx + 1; childIdx < endChildIdx; childIdx++) {
     198           0 :     Accessible* child = GetChildAt(childIdx);
     199           0 :     child->AppendTextTo(aText);
     200             :   }
     201             : 
     202           0 :   int32_t endChildOffset =  GetChildOffset(endChildIdx);
     203           0 :   if (endChildOffset == -1)
     204           0 :     return;
     205             : 
     206           0 :   Accessible* endChild = GetChildAt(endChildIdx);
     207           0 :   endChild->AppendTextTo(aText, 0, endOffset - endChildOffset);
     208             : }
     209             : 
     210             : uint32_t
     211           0 : HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
     212             :                                       bool aIsEndOffset) const
     213             : {
     214           0 :   if (!aNode)
     215           0 :     return 0;
     216             : 
     217           0 :   uint32_t offset = 0;
     218           0 :   nsINode* findNode = nullptr;
     219             : 
     220           0 :   if (aNodeOffset == -1) {
     221           0 :     findNode = aNode;
     222             : 
     223           0 :   } else if (aNode->IsNodeOfType(nsINode::eTEXT)) {
     224             :     // For text nodes, aNodeOffset comes in as a character offset
     225             :     // Text offset will be added at the end, if we find the offset in this hypertext
     226             :     // We want the "skipped" offset into the text (rendered text without the extra whitespace)
     227           0 :     nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
     228           0 :     NS_ENSURE_TRUE(frame, 0);
     229             : 
     230           0 :     nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &offset);
     231           0 :     NS_ENSURE_SUCCESS(rv, 0);
     232             : 
     233           0 :     findNode = aNode;
     234             : 
     235             :   } else {
     236             :     // findNode could be null if aNodeOffset == # of child nodes, which means
     237             :     // one of two things:
     238             :     // 1) there are no children, and the passed-in node is not mContent -- use
     239             :     //    parentContent for the node to find
     240             :     // 2) there are no children and the passed-in node is mContent, which means
     241             :     //    we're an empty nsIAccessibleText
     242             :     // 3) there are children and we're at the end of the children
     243             : 
     244           0 :     findNode = aNode->GetChildAt(aNodeOffset);
     245           0 :     if (!findNode) {
     246           0 :       if (aNodeOffset == 0) {
     247           0 :         if (aNode == GetNode()) {
     248             :           // Case #1: this accessible has no children and thus has empty text,
     249             :           // we can only be at hypertext offset 0.
     250           0 :           return 0;
     251             :         }
     252             : 
     253             :         // Case #2: there are no children, we're at this node.
     254           0 :         findNode = aNode;
     255           0 :       } else if (aNodeOffset == static_cast<int32_t>(aNode->GetChildCount())) {
     256             :         // Case #3: we're after the last child, get next node to this one.
     257           0 :         for (nsINode* tmpNode = aNode;
     258           0 :              !findNode && tmpNode && tmpNode != mContent;
     259             :              tmpNode = tmpNode->GetParent()) {
     260           0 :           findNode = tmpNode->GetNextSibling();
     261             :         }
     262             :       }
     263             :     }
     264             :   }
     265             : 
     266             :   // Get accessible for this findNode, or if that node isn't accessible, use the
     267             :   // accessible for the next DOM node which has one (based on forward depth first search)
     268           0 :   Accessible* descendant = nullptr;
     269           0 :   if (findNode) {
     270           0 :     nsCOMPtr<nsIContent> findContent(do_QueryInterface(findNode));
     271           0 :     if (findContent && findContent->IsHTMLElement() &&
     272           0 :         findContent->NodeInfo()->Equals(nsGkAtoms::br) &&
     273           0 :         findContent->AttrValueIs(kNameSpaceID_None,
     274             :                                  nsGkAtoms::mozeditorbogusnode,
     275             :                                  nsGkAtoms::_true,
     276             :                                  eIgnoreCase)) {
     277             :       // This <br> is the hacky "bogus node" used when there is no text in a control
     278           0 :       return 0;
     279             :     }
     280             : 
     281           0 :     descendant = mDoc->GetAccessible(findNode);
     282           0 :     if (!descendant && findNode->IsContent()) {
     283           0 :       Accessible* container = mDoc->GetContainerAccessible(findNode);
     284           0 :       if (container) {
     285             :         TreeWalker walker(container, findNode->AsContent(),
     286           0 :                           TreeWalker::eWalkContextTree);
     287           0 :         descendant = walker.Next();
     288           0 :         if (!descendant)
     289           0 :           descendant = container;
     290             :       }
     291             :     }
     292             :   }
     293             : 
     294           0 :   return TransformOffset(descendant, offset, aIsEndOffset);
     295             : }
     296             : 
     297             : uint32_t
     298           0 : HyperTextAccessible::TransformOffset(Accessible* aDescendant,
     299             :                                      uint32_t aOffset, bool aIsEndOffset) const
     300             : {
     301             :   // From the descendant, go up and get the immediate child of this hypertext.
     302           0 :   uint32_t offset = aOffset;
     303           0 :   Accessible* descendant = aDescendant;
     304           0 :   while (descendant) {
     305           0 :     Accessible* parent = descendant->Parent();
     306           0 :     if (parent == this)
     307           0 :       return GetChildOffset(descendant) + offset;
     308             : 
     309             :     // This offset no longer applies because the passed-in text object is not
     310             :     // a child of the hypertext. This happens when there are nested hypertexts,
     311             :     // e.g. <div>abc<h1>def</h1>ghi</div>. Thus we need to adjust the offset
     312             :     // to make it relative the hypertext.
     313             :     // If the end offset is not supposed to be inclusive and the original point
     314             :     // is not at 0 offset then the returned offset should be after an embedded
     315             :     // character the original point belongs to.
     316           0 :     if (aIsEndOffset)
     317           0 :       offset = (offset > 0 || descendant->IndexInParent() > 0) ? 1 : 0;
     318             :     else
     319           0 :       offset = 0;
     320             : 
     321           0 :     descendant = parent;
     322             :   }
     323             : 
     324             :   // If the given a11y point cannot be mapped into offset relative this hypertext
     325             :   // offset then return length as fallback value.
     326           0 :   return CharacterCount();
     327             : }
     328             : 
     329             : /**
     330             :  * GetElementAsContentOf() returns a content representing an element which is
     331             :  * or includes aNode.
     332             :  *
     333             :  * XXX This method is enough to retrieve ::before or ::after pseudo element.
     334             :  *     So, if you want to use this for other purpose, you might need to check
     335             :  *     ancestors too.
     336             :  */
     337           0 : static nsIContent* GetElementAsContentOf(nsINode* aNode)
     338             : {
     339           0 :   if (aNode->IsElement()) {
     340           0 :     return aNode->AsContent();
     341             :   }
     342           0 :   nsIContent* parent = aNode->GetParent();
     343           0 :   return parent && parent->IsElement() ? parent : nullptr;
     344             : }
     345             : 
     346             : bool
     347           0 : HyperTextAccessible::OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
     348             :                                        nsRange* aRange)
     349             : {
     350           0 :   DOMPoint startPoint = OffsetToDOMPoint(aStartOffset);
     351           0 :   if (!startPoint.node)
     352           0 :     return false;
     353             : 
     354             :   // HyperTextAccessible manages pseudo elements generated by ::before or
     355             :   // ::after.  However, contents of them are not in the DOM tree normally.
     356             :   // Therefore, they are not selectable and editable.  So, when this creates
     357             :   // a DOM range, it should not start from nor end in any pseudo contents.
     358             : 
     359           0 :   nsIContent* container = GetElementAsContentOf(startPoint.node);
     360             :   DOMPoint startPointForDOMRange =
     361           0 :     ClosestNotGeneratedDOMPoint(startPoint, container);
     362           0 :   aRange->SetStart(startPointForDOMRange.node, startPointForDOMRange.idx);
     363             : 
     364             :   // If the caller wants collapsed range, let's collapse the range to its start.
     365           0 :   if (aStartOffset == aEndOffset) {
     366           0 :     aRange->Collapse(true);
     367           0 :     return true;
     368             :   }
     369             : 
     370           0 :   DOMPoint endPoint = OffsetToDOMPoint(aEndOffset);
     371           0 :   if (!endPoint.node)
     372           0 :     return false;
     373             : 
     374           0 :   if (startPoint.node != endPoint.node) {
     375           0 :     container = GetElementAsContentOf(endPoint.node);
     376             :   }
     377             : 
     378             :   DOMPoint endPointForDOMRange =
     379           0 :     ClosestNotGeneratedDOMPoint(endPoint, container);
     380           0 :   aRange->SetEnd(endPointForDOMRange.node, endPointForDOMRange.idx);
     381           0 :   return true;
     382             : }
     383             : 
     384             : DOMPoint
     385           0 : HyperTextAccessible::OffsetToDOMPoint(int32_t aOffset)
     386             : {
     387             :   // 0 offset is valid even if no children. In this case the associated editor
     388             :   // is empty so return a DOM point for editor root element.
     389           0 :   if (aOffset == 0) {
     390           0 :     nsCOMPtr<nsIEditor> editor = GetEditor();
     391           0 :     if (editor) {
     392           0 :       bool isEmpty = false;
     393           0 :       editor->GetDocumentIsEmpty(&isEmpty);
     394           0 :       if (isEmpty) {
     395           0 :         nsCOMPtr<nsIDOMElement> editorRootElm;
     396           0 :         editor->GetRootElement(getter_AddRefs(editorRootElm));
     397             : 
     398           0 :         nsCOMPtr<nsINode> editorRoot(do_QueryInterface(editorRootElm));
     399           0 :         return DOMPoint(editorRoot, 0);
     400             :       }
     401             :     }
     402             :   }
     403             : 
     404           0 :   int32_t childIdx = GetChildIndexAtOffset(aOffset);
     405           0 :   if (childIdx == -1)
     406           0 :     return DOMPoint();
     407             : 
     408           0 :   Accessible* child = GetChildAt(childIdx);
     409           0 :   int32_t innerOffset = aOffset - GetChildOffset(childIdx);
     410             : 
     411             :   // A text leaf case.
     412           0 :   if (child->IsTextLeaf()) {
     413             :     // The point is inside the text node. This is always true for any text leaf
     414             :     // except a last child one. See assertion below.
     415           0 :     if (aOffset < GetChildOffset(childIdx + 1)) {
     416           0 :       nsIContent* content = child->GetContent();
     417           0 :       int32_t idx = 0;
     418           0 :       if (NS_FAILED(RenderedToContentOffset(content->GetPrimaryFrame(),
     419             :                                             innerOffset, &idx)))
     420           0 :         return DOMPoint();
     421             : 
     422           0 :       return DOMPoint(content, idx);
     423             :     }
     424             : 
     425             :     // Set the DOM point right after the text node.
     426           0 :     MOZ_ASSERT(static_cast<uint32_t>(aOffset) == CharacterCount());
     427           0 :     innerOffset = 1;
     428             :   }
     429             : 
     430             :   // Case of embedded object. The point is either before or after the element.
     431           0 :   NS_ASSERTION(innerOffset == 0 || innerOffset == 1, "A wrong inner offset!");
     432           0 :   nsINode* node = child->GetNode();
     433           0 :   nsINode* parentNode = node->GetParentNode();
     434             :   return parentNode ?
     435           0 :     DOMPoint(parentNode, parentNode->IndexOf(node) + innerOffset) :
     436           0 :     DOMPoint();
     437             : }
     438             : 
     439             : DOMPoint
     440           0 : HyperTextAccessible::ClosestNotGeneratedDOMPoint(const DOMPoint& aDOMPoint,
     441             :                                                  nsIContent* aElementContent)
     442             : {
     443           0 :   MOZ_ASSERT(aDOMPoint.node, "The node must not be null");
     444             : 
     445             :   // ::before pseudo element
     446           0 :   if (aElementContent &&
     447           0 :       aElementContent->IsGeneratedContentContainerForBefore()) {
     448           0 :     MOZ_ASSERT(aElementContent->GetParent(),
     449             :                "::before must have parent element");
     450             :     // The first child of its parent (i.e., immediately after the ::before) is
     451             :     // good point for a DOM range.
     452           0 :     return DOMPoint(aElementContent->GetParent(), 0);
     453             :   }
     454             : 
     455             :   // ::after pseudo element
     456           0 :   if (aElementContent &&
     457           0 :       aElementContent->IsGeneratedContentContainerForAfter()) {
     458           0 :     MOZ_ASSERT(aElementContent->GetParent(),
     459             :                "::after must have parent element");
     460             :     // The end of its parent (i.e., immediately before the ::after) is good
     461             :     // point for a DOM range.
     462           0 :     return DOMPoint(aElementContent->GetParent(),
     463           0 :                     aElementContent->GetParent()->GetChildCount());
     464             :   }
     465             : 
     466           0 :   return aDOMPoint;
     467             : }
     468             : 
     469             : uint32_t
     470           0 : HyperTextAccessible::FindOffset(uint32_t aOffset, nsDirection aDirection,
     471             :                                 nsSelectionAmount aAmount,
     472             :                                 EWordMovementType aWordMovementType)
     473             : {
     474           0 :   NS_ASSERTION(aDirection == eDirPrevious || aAmount != eSelectBeginLine,
     475             :                "eSelectBeginLine should only be used with eDirPrevious");
     476             : 
     477             :   // Find a leaf accessible frame to start with. PeekOffset wants this.
     478           0 :   HyperTextAccessible* text = this;
     479           0 :   Accessible* child = nullptr;
     480           0 :   int32_t innerOffset = aOffset;
     481             : 
     482           0 :   do {
     483           0 :     int32_t childIdx = text->GetChildIndexAtOffset(innerOffset);
     484             : 
     485             :     // We can have an empty text leaf as our only child. Since empty text
     486             :     // leaves are not accessible we then have no children, but 0 is a valid
     487             :     // innerOffset.
     488           0 :     if (childIdx == -1) {
     489           0 :       NS_ASSERTION(innerOffset == 0 && !text->ChildCount(), "No childIdx?");
     490           0 :       return DOMPointToOffset(text->GetNode(), 0, aDirection == eDirNext);
     491             :     }
     492             : 
     493           0 :     child = text->GetChildAt(childIdx);
     494             : 
     495             :     // HTML list items may need special processing because PeekOffset doesn't
     496             :     // work with list bullets.
     497           0 :     if (text->IsHTMLListItem()) {
     498           0 :       HTMLLIAccessible* li = text->AsHTMLListItem();
     499           0 :       if (child == li->Bullet()) {
     500             :         // XXX: the logic is broken for multichar bullets in moving by
     501             :         // char/cluster/word cases.
     502           0 :         if (text != this) {
     503           0 :           return aDirection == eDirPrevious ?
     504             :             TransformOffset(text, 0, false) :
     505           0 :             TransformOffset(text, 1, true);
     506             :         }
     507           0 :         if (aDirection == eDirPrevious)
     508           0 :           return 0;
     509             : 
     510           0 :         uint32_t nextOffset = GetChildOffset(1);
     511           0 :         if (nextOffset == 0)
     512           0 :           return 0;
     513             : 
     514           0 :         switch (aAmount) {
     515             :           case eSelectLine:
     516             :           case eSelectEndLine:
     517             :             // Ask a text leaf next (if not empty) to the bullet for an offset
     518             :             // since list item may be multiline.
     519           0 :             return nextOffset < CharacterCount() ?
     520             :               FindOffset(nextOffset, aDirection, aAmount, aWordMovementType) :
     521           0 :               nextOffset;
     522             : 
     523             :           default:
     524           0 :             return nextOffset;
     525             :         }
     526             :       }
     527             :     }
     528             : 
     529           0 :     innerOffset -= text->GetChildOffset(childIdx);
     530             : 
     531           0 :     text = child->AsHyperText();
     532           0 :   } while (text);
     533             : 
     534           0 :   nsIFrame* childFrame = child->GetFrame();
     535           0 :   if (!childFrame) {
     536           0 :     NS_ERROR("No child frame");
     537           0 :     return 0;
     538             :   }
     539             : 
     540           0 :   int32_t innerContentOffset = innerOffset;
     541           0 :   if (child->IsTextLeaf()) {
     542           0 :     NS_ASSERTION(childFrame->IsTextFrame(), "Wrong frame!");
     543           0 :     RenderedToContentOffset(childFrame, innerOffset, &innerContentOffset);
     544             :   }
     545             : 
     546           0 :   nsIFrame* frameAtOffset = childFrame;
     547           0 :   int32_t unusedOffsetInFrame = 0;
     548             :   childFrame->GetChildFrameContainingOffset(innerContentOffset, true,
     549             :                                             &unusedOffsetInFrame,
     550           0 :                                             &frameAtOffset);
     551             : 
     552           0 :   const bool kIsJumpLinesOk = true; // okay to jump lines
     553           0 :   const bool kIsScrollViewAStop = false; // do not stop at scroll views
     554           0 :   const bool kIsKeyboardSelect = true; // is keyboard selection
     555           0 :   const bool kIsVisualBidi = false; // use visual order for bidi text
     556             :   nsPeekOffsetStruct pos(aAmount, aDirection, innerContentOffset,
     557           0 :                          nsPoint(0, 0), kIsJumpLinesOk, kIsScrollViewAStop,
     558             :                          kIsKeyboardSelect, kIsVisualBidi,
     559           0 :                          false, aWordMovementType);
     560           0 :   nsresult rv = frameAtOffset->PeekOffset(&pos);
     561             : 
     562             :   // PeekOffset fails on last/first lines of the text in certain cases.
     563           0 :   if (NS_FAILED(rv) && aAmount == eSelectLine) {
     564           0 :     pos.mAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
     565           0 :     frameAtOffset->PeekOffset(&pos);
     566             :   }
     567           0 :   if (!pos.mResultContent) {
     568           0 :     NS_ERROR("No result content!");
     569           0 :     return 0;
     570             :   }
     571             : 
     572             :   // Turn the resulting DOM point into an offset.
     573           0 :   uint32_t hyperTextOffset = DOMPointToOffset(pos.mResultContent,
     574             :                                               pos.mContentOffset,
     575           0 :                                               aDirection == eDirNext);
     576             : 
     577           0 :   if (aDirection == eDirPrevious) {
     578             :     // If we reached the end during search, this means we didn't find the DOM point
     579             :     // and we're actually at the start of the paragraph
     580           0 :     if (hyperTextOffset == CharacterCount())
     581           0 :       return 0;
     582             : 
     583             :     // PeekOffset stops right before bullet so return 0 to workaround it.
     584           0 :     if (IsHTMLListItem() && aAmount == eSelectBeginLine &&
     585             :         hyperTextOffset > 0) {
     586           0 :       Accessible* prevOffsetChild = GetChildAtOffset(hyperTextOffset - 1);
     587           0 :       if (prevOffsetChild == AsHTMLListItem()->Bullet())
     588           0 :         return 0;
     589             :     }
     590             :   }
     591             : 
     592           0 :   return hyperTextOffset;
     593             : }
     594             : 
     595             : uint32_t
     596           0 : HyperTextAccessible::FindLineBoundary(uint32_t aOffset,
     597             :                                       EWhichLineBoundary aWhichLineBoundary)
     598             : {
     599             :   // Note: empty last line doesn't have own frame (a previous line contains '\n'
     600             :   // character instead) thus when it makes a difference we need to process this
     601             :   // case separately (otherwise operations are performed on previous line).
     602           0 :   switch (aWhichLineBoundary) {
     603             :     case ePrevLineBegin: {
     604             :       // Fetch a previous line and move to its start (as arrow up and home keys
     605             :       // were pressed).
     606           0 :       if (IsEmptyLastLineOffset(aOffset))
     607           0 :         return FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
     608             : 
     609           0 :       uint32_t tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectLine);
     610           0 :       return FindOffset(tmpOffset, eDirPrevious, eSelectBeginLine);
     611             :     }
     612             : 
     613             :     case ePrevLineEnd: {
     614           0 :       if (IsEmptyLastLineOffset(aOffset))
     615           0 :         return aOffset - 1;
     616             : 
     617             :       // If offset is at first line then return 0 (first line start).
     618           0 :       uint32_t tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
     619           0 :       if (tmpOffset == 0)
     620           0 :         return 0;
     621             : 
     622             :       // Otherwise move to end of previous line (as arrow up and end keys were
     623             :       // pressed).
     624           0 :       tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectLine);
     625           0 :       return FindOffset(tmpOffset, eDirNext, eSelectEndLine);
     626             :     }
     627             : 
     628             :     case eThisLineBegin:
     629           0 :       if (IsEmptyLastLineOffset(aOffset))
     630           0 :         return aOffset;
     631             : 
     632             :       // Move to begin of the current line (as home key was pressed).
     633           0 :       return FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
     634             : 
     635             :     case eThisLineEnd:
     636           0 :       if (IsEmptyLastLineOffset(aOffset))
     637           0 :         return aOffset;
     638             : 
     639             :       // Move to end of the current line (as end key was pressed).
     640           0 :       return FindOffset(aOffset, eDirNext, eSelectEndLine);
     641             : 
     642             :     case eNextLineBegin: {
     643           0 :       if (IsEmptyLastLineOffset(aOffset))
     644           0 :         return aOffset;
     645             : 
     646             :       // Move to begin of the next line if any (arrow down and home keys),
     647             :       // otherwise end of the current line (arrow down only).
     648           0 :       uint32_t tmpOffset = FindOffset(aOffset, eDirNext, eSelectLine);
     649           0 :       if (tmpOffset == CharacterCount())
     650           0 :         return tmpOffset;
     651             : 
     652           0 :       return FindOffset(tmpOffset, eDirPrevious, eSelectBeginLine);
     653             :     }
     654             : 
     655             :     case eNextLineEnd: {
     656           0 :       if (IsEmptyLastLineOffset(aOffset))
     657           0 :         return aOffset;
     658             : 
     659             :       // Move to next line end (as down arrow and end key were pressed).
     660           0 :       uint32_t tmpOffset = FindOffset(aOffset, eDirNext, eSelectLine);
     661           0 :       if (tmpOffset == CharacterCount())
     662           0 :         return tmpOffset;
     663             : 
     664           0 :       return FindOffset(tmpOffset, eDirNext, eSelectEndLine);
     665             :     }
     666             :   }
     667             : 
     668           0 :   return 0;
     669             : }
     670             : 
     671             : void
     672           0 : HyperTextAccessible::TextBeforeOffset(int32_t aOffset,
     673             :                                       AccessibleTextBoundary aBoundaryType,
     674             :                                       int32_t* aStartOffset, int32_t* aEndOffset,
     675             :                                       nsAString& aText)
     676             : {
     677           0 :   *aStartOffset = *aEndOffset = 0;
     678           0 :   aText.Truncate();
     679             : 
     680           0 :   index_t convertedOffset = ConvertMagicOffset(aOffset);
     681           0 :   if (!convertedOffset.IsValid() || convertedOffset > CharacterCount()) {
     682           0 :     NS_ERROR("Wrong in offset!");
     683           0 :     return;
     684             :   }
     685             : 
     686           0 :   uint32_t adjustedOffset = convertedOffset;
     687           0 :   if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
     688           0 :     adjustedOffset = AdjustCaretOffset(adjustedOffset);
     689             : 
     690           0 :   switch (aBoundaryType) {
     691             :     case nsIAccessibleText::BOUNDARY_CHAR:
     692           0 :       if (convertedOffset != 0)
     693           0 :         CharAt(convertedOffset - 1, aText, aStartOffset, aEndOffset);
     694           0 :       break;
     695             : 
     696             :     case nsIAccessibleText::BOUNDARY_WORD_START: {
     697             :       // If the offset is a word start (except text length offset) then move
     698             :       // backward to find a start offset (end offset is the given offset).
     699             :       // Otherwise move backward twice to find both start and end offsets.
     700           0 :       if (adjustedOffset == CharacterCount()) {
     701           0 :         *aEndOffset = FindWordBoundary(adjustedOffset, eDirPrevious, eStartWord);
     702           0 :         *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
     703             :       } else {
     704           0 :         *aStartOffset = FindWordBoundary(adjustedOffset, eDirPrevious, eStartWord);
     705           0 :         *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eStartWord);
     706           0 :         if (*aEndOffset != static_cast<int32_t>(adjustedOffset)) {
     707           0 :           *aEndOffset = *aStartOffset;
     708           0 :           *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
     709             :         }
     710             :       }
     711           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     712           0 :       break;
     713             :     }
     714             : 
     715             :     case nsIAccessibleText::BOUNDARY_WORD_END: {
     716             :       // Move word backward twice to find start and end offsets.
     717           0 :       *aEndOffset = FindWordBoundary(convertedOffset, eDirPrevious, eEndWord);
     718           0 :       *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
     719           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     720           0 :       break;
     721             :     }
     722             : 
     723             :     case nsIAccessibleText::BOUNDARY_LINE_START:
     724           0 :       *aStartOffset = FindLineBoundary(adjustedOffset, ePrevLineBegin);
     725           0 :       *aEndOffset = FindLineBoundary(adjustedOffset, eThisLineBegin);
     726           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     727           0 :       break;
     728             : 
     729             :     case nsIAccessibleText::BOUNDARY_LINE_END: {
     730           0 :       *aEndOffset = FindLineBoundary(adjustedOffset, ePrevLineEnd);
     731           0 :       int32_t tmpOffset = *aEndOffset;
     732             :       // Adjust offset if line is wrapped.
     733           0 :       if (*aEndOffset != 0 && !IsLineEndCharAt(*aEndOffset))
     734           0 :         tmpOffset--;
     735             : 
     736           0 :       *aStartOffset = FindLineBoundary(tmpOffset, ePrevLineEnd);
     737           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     738           0 :       break;
     739             :     }
     740             :   }
     741             : }
     742             : 
     743             : void
     744           0 : HyperTextAccessible::TextAtOffset(int32_t aOffset,
     745             :                                   AccessibleTextBoundary aBoundaryType,
     746             :                                   int32_t* aStartOffset, int32_t* aEndOffset,
     747             :                                   nsAString& aText)
     748             : {
     749           0 :   *aStartOffset = *aEndOffset = 0;
     750           0 :   aText.Truncate();
     751             : 
     752           0 :   uint32_t adjustedOffset = ConvertMagicOffset(aOffset);
     753           0 :   if (adjustedOffset == std::numeric_limits<uint32_t>::max()) {
     754           0 :     NS_ERROR("Wrong given offset!");
     755           0 :     return;
     756             :   }
     757             : 
     758           0 :   switch (aBoundaryType) {
     759             :     case nsIAccessibleText::BOUNDARY_CHAR:
     760             :       // Return no char if caret is at the end of wrapped line (case of no line
     761             :       // end character). Returning a next line char is confusing for AT.
     762           0 :       if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET && IsCaretAtEndOfLine())
     763           0 :         *aStartOffset = *aEndOffset = adjustedOffset;
     764             :       else
     765           0 :         CharAt(adjustedOffset, aText, aStartOffset, aEndOffset);
     766           0 :       break;
     767             : 
     768             :     case nsIAccessibleText::BOUNDARY_WORD_START:
     769           0 :       if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
     770           0 :         adjustedOffset = AdjustCaretOffset(adjustedOffset);
     771             : 
     772           0 :       *aEndOffset = FindWordBoundary(adjustedOffset, eDirNext, eStartWord);
     773           0 :       *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
     774           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     775           0 :       break;
     776             : 
     777             :     case nsIAccessibleText::BOUNDARY_WORD_END:
     778             :       // Ignore the spec and follow what WebKitGtk does because Orca expects it,
     779             :       // i.e. return a next word at word end offset of the current word
     780             :       // (WebKitGtk behavior) instead the current word (AKT spec).
     781           0 :       *aEndOffset = FindWordBoundary(adjustedOffset, eDirNext, eEndWord);
     782           0 :       *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
     783           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     784           0 :       break;
     785             : 
     786             :     case nsIAccessibleText::BOUNDARY_LINE_START:
     787           0 :       if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
     788           0 :         adjustedOffset = AdjustCaretOffset(adjustedOffset);
     789             : 
     790           0 :       *aStartOffset = FindLineBoundary(adjustedOffset, eThisLineBegin);
     791           0 :       *aEndOffset = FindLineBoundary(adjustedOffset, eNextLineBegin);
     792           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     793           0 :       break;
     794             : 
     795             :     case nsIAccessibleText::BOUNDARY_LINE_END:
     796           0 :       if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
     797           0 :         adjustedOffset = AdjustCaretOffset(adjustedOffset);
     798             : 
     799             :       // In contrast to word end boundary we follow the spec here.
     800           0 :       *aStartOffset = FindLineBoundary(adjustedOffset, ePrevLineEnd);
     801           0 :       *aEndOffset = FindLineBoundary(adjustedOffset, eThisLineEnd);
     802           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     803           0 :       break;
     804             :   }
     805             : }
     806             : 
     807             : void
     808           0 : HyperTextAccessible::TextAfterOffset(int32_t aOffset,
     809             :                                      AccessibleTextBoundary aBoundaryType,
     810             :                                      int32_t* aStartOffset, int32_t* aEndOffset,
     811             :                                      nsAString& aText)
     812             : {
     813           0 :   *aStartOffset = *aEndOffset = 0;
     814           0 :   aText.Truncate();
     815             : 
     816           0 :   index_t convertedOffset = ConvertMagicOffset(aOffset);
     817           0 :   if (!convertedOffset.IsValid() || convertedOffset > CharacterCount()) {
     818           0 :     NS_ERROR("Wrong in offset!");
     819           0 :     return;
     820             :   }
     821             : 
     822           0 :   uint32_t adjustedOffset = convertedOffset;
     823           0 :   if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
     824           0 :     adjustedOffset = AdjustCaretOffset(adjustedOffset);
     825             : 
     826           0 :   switch (aBoundaryType) {
     827             :     case nsIAccessibleText::BOUNDARY_CHAR:
     828             :       // If caret is at the end of wrapped line (case of no line end character)
     829             :       // then char after the offset is a first char at next line.
     830           0 :       if (adjustedOffset >= CharacterCount())
     831           0 :         *aStartOffset = *aEndOffset = CharacterCount();
     832             :       else
     833           0 :         CharAt(adjustedOffset + 1, aText, aStartOffset, aEndOffset);
     834           0 :       break;
     835             : 
     836             :     case nsIAccessibleText::BOUNDARY_WORD_START:
     837             :       // Move word forward twice to find start and end offsets.
     838           0 :       *aStartOffset = FindWordBoundary(adjustedOffset, eDirNext, eStartWord);
     839           0 :       *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eStartWord);
     840           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     841           0 :       break;
     842             : 
     843             :     case nsIAccessibleText::BOUNDARY_WORD_END:
     844             :       // If the offset is a word end (except 0 offset) then move forward to find
     845             :       // end offset (start offset is the given offset). Otherwise move forward
     846             :       // twice to find both start and end offsets.
     847           0 :       if (convertedOffset == 0) {
     848           0 :         *aStartOffset = FindWordBoundary(convertedOffset, eDirNext, eEndWord);
     849           0 :         *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
     850             :       } else {
     851           0 :         *aEndOffset = FindWordBoundary(convertedOffset, eDirNext, eEndWord);
     852           0 :         *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
     853           0 :         if (*aStartOffset != static_cast<int32_t>(convertedOffset)) {
     854           0 :           *aStartOffset = *aEndOffset;
     855           0 :           *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
     856             :         }
     857             :       }
     858           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     859           0 :       break;
     860             : 
     861             :     case nsIAccessibleText::BOUNDARY_LINE_START:
     862           0 :       *aStartOffset = FindLineBoundary(adjustedOffset, eNextLineBegin);
     863           0 :       *aEndOffset = FindLineBoundary(*aStartOffset, eNextLineBegin);
     864           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     865           0 :       break;
     866             : 
     867             :     case nsIAccessibleText::BOUNDARY_LINE_END:
     868           0 :       *aStartOffset = FindLineBoundary(adjustedOffset, eThisLineEnd);
     869           0 :       *aEndOffset = FindLineBoundary(adjustedOffset, eNextLineEnd);
     870           0 :       TextSubstring(*aStartOffset, *aEndOffset, aText);
     871           0 :       break;
     872             :   }
     873             : }
     874             : 
     875             : already_AddRefed<nsIPersistentProperties>
     876           0 : HyperTextAccessible::TextAttributes(bool aIncludeDefAttrs, int32_t aOffset,
     877             :                                        int32_t* aStartOffset,
     878             :                                        int32_t* aEndOffset)
     879             : {
     880             :   // 1. Get each attribute and its ranges one after another.
     881             :   // 2. As we get each new attribute, we pass the current start and end offsets
     882             :   //    as in/out parameters. In other words, as attributes are collected,
     883             :   //    the attribute range itself can only stay the same or get smaller.
     884             : 
     885           0 :   *aStartOffset = *aEndOffset = 0;
     886           0 :   index_t offset = ConvertMagicOffset(aOffset);
     887           0 :   if (!offset.IsValid() || offset > CharacterCount()) {
     888           0 :     NS_ERROR("Wrong in offset!");
     889           0 :     return nullptr;
     890             :   }
     891             : 
     892             :   nsCOMPtr<nsIPersistentProperties> attributes =
     893           0 :     do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
     894             : 
     895           0 :   Accessible* accAtOffset = GetChildAtOffset(offset);
     896           0 :   if (!accAtOffset) {
     897             :     // Offset 0 is correct offset when accessible has empty text. Include
     898             :     // default attributes if they were requested, otherwise return empty set.
     899           0 :     if (offset == 0) {
     900           0 :       if (aIncludeDefAttrs) {
     901           0 :         TextAttrsMgr textAttrsMgr(this);
     902           0 :         textAttrsMgr.GetAttributes(attributes);
     903             :       }
     904           0 :       return attributes.forget();
     905             :     }
     906           0 :     return nullptr;
     907             :   }
     908             : 
     909           0 :   int32_t accAtOffsetIdx = accAtOffset->IndexInParent();
     910           0 :   uint32_t startOffset = GetChildOffset(accAtOffsetIdx);
     911           0 :   uint32_t endOffset = GetChildOffset(accAtOffsetIdx + 1);
     912           0 :   int32_t offsetInAcc = offset - startOffset;
     913             : 
     914             :   TextAttrsMgr textAttrsMgr(this, aIncludeDefAttrs, accAtOffset,
     915           0 :                             accAtOffsetIdx);
     916           0 :   textAttrsMgr.GetAttributes(attributes, &startOffset, &endOffset);
     917             : 
     918             :   // Compute spelling attributes on text accessible only.
     919           0 :   nsIFrame *offsetFrame = accAtOffset->GetFrame();
     920           0 :   if (offsetFrame && offsetFrame->IsTextFrame()) {
     921           0 :     int32_t nodeOffset = 0;
     922           0 :     RenderedToContentOffset(offsetFrame, offsetInAcc, &nodeOffset);
     923             : 
     924             :     // Set 'misspelled' text attribute.
     925           0 :     GetSpellTextAttr(accAtOffset->GetNode(), nodeOffset,
     926           0 :                      &startOffset, &endOffset, attributes);
     927             :   }
     928             : 
     929           0 :   *aStartOffset = startOffset;
     930           0 :   *aEndOffset = endOffset;
     931           0 :   return attributes.forget();
     932             : }
     933             : 
     934             : already_AddRefed<nsIPersistentProperties>
     935           0 : HyperTextAccessible::DefaultTextAttributes()
     936             : {
     937             :   nsCOMPtr<nsIPersistentProperties> attributes =
     938           0 :     do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
     939             : 
     940           0 :   TextAttrsMgr textAttrsMgr(this);
     941           0 :   textAttrsMgr.GetAttributes(attributes);
     942           0 :   return attributes.forget();
     943             : }
     944             : 
     945             : int32_t
     946           0 : HyperTextAccessible::GetLevelInternal()
     947             : {
     948           0 :   if (mContent->IsHTMLElement(nsGkAtoms::h1))
     949           0 :     return 1;
     950           0 :   if (mContent->IsHTMLElement(nsGkAtoms::h2))
     951           0 :     return 2;
     952           0 :   if (mContent->IsHTMLElement(nsGkAtoms::h3))
     953           0 :     return 3;
     954           0 :   if (mContent->IsHTMLElement(nsGkAtoms::h4))
     955           0 :     return 4;
     956           0 :   if (mContent->IsHTMLElement(nsGkAtoms::h5))
     957           0 :     return 5;
     958           0 :   if (mContent->IsHTMLElement(nsGkAtoms::h6))
     959           0 :     return 6;
     960             : 
     961           0 :   return AccessibleWrap::GetLevelInternal();
     962             : }
     963             : 
     964             : void
     965           0 : HyperTextAccessible::SetMathMLXMLRoles(nsIPersistentProperties* aAttributes)
     966             : {
     967             :   // Add MathML xmlroles based on the position inside the parent.
     968           0 :   Accessible* parent = Parent();
     969           0 :   if (parent) {
     970           0 :     switch (parent->Role()) {
     971             :     case roles::MATHML_CELL:
     972             :     case roles::MATHML_ENCLOSED:
     973             :     case roles::MATHML_ERROR:
     974             :     case roles::MATHML_MATH:
     975             :     case roles::MATHML_ROW:
     976             :     case roles::MATHML_SQUARE_ROOT:
     977             :     case roles::MATHML_STYLE:
     978           0 :       if (Role() == roles::MATHML_OPERATOR) {
     979             :         // This is an operator inside an <mrow> (or an inferred <mrow>).
     980             :         // See http://www.w3.org/TR/MathML3/chapter3.html#presm.inferredmrow
     981             :         // XXX We should probably do something similar for MATHML_FENCED, but
     982             :         // operators do not appear in the accessible tree. See bug 1175747.
     983           0 :         nsIMathMLFrame* mathMLFrame = do_QueryFrame(GetFrame());
     984           0 :         if (mathMLFrame) {
     985           0 :           nsEmbellishData embellishData;
     986           0 :           mathMLFrame->GetEmbellishData(embellishData);
     987           0 :           if (NS_MATHML_EMBELLISH_IS_FENCE(embellishData.flags)) {
     988           0 :             if (!PrevSibling()) {
     989             :               nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
     990           0 :                                      nsGkAtoms::open_fence);
     991           0 :             } else if (!NextSibling()) {
     992             :               nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
     993           0 :                                      nsGkAtoms::close_fence);
     994             :             }
     995             :           }
     996           0 :           if (NS_MATHML_EMBELLISH_IS_SEPARATOR(embellishData.flags)) {
     997             :             nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
     998           0 :                                    nsGkAtoms::separator_);
     999             :           }
    1000             :         }
    1001             :       }
    1002           0 :     break;
    1003             :     case roles::MATHML_FRACTION:
    1004           0 :       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1005           0 :                              IndexInParent() == 0 ?
    1006             :                              nsGkAtoms::numerator :
    1007           0 :                              nsGkAtoms::denominator);
    1008           0 :       break;
    1009             :     case roles::MATHML_ROOT:
    1010           0 :       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1011           0 :                              IndexInParent() == 0 ? nsGkAtoms::base :
    1012           0 :                              nsGkAtoms::root_index);
    1013           0 :       break;
    1014             :     case roles::MATHML_SUB:
    1015           0 :       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1016           0 :                              IndexInParent() == 0 ? nsGkAtoms::base :
    1017           0 :                              nsGkAtoms::subscript);
    1018           0 :       break;
    1019             :     case roles::MATHML_SUP:
    1020           0 :       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1021           0 :                              IndexInParent() == 0 ? nsGkAtoms::base :
    1022           0 :                              nsGkAtoms::superscript);
    1023           0 :       break;
    1024             :     case roles::MATHML_SUB_SUP: {
    1025           0 :       int32_t index = IndexInParent();
    1026           0 :       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1027             :                              index == 0 ? nsGkAtoms::base :
    1028           0 :                              (index == 1 ? nsGkAtoms::subscript :
    1029           0 :                               nsGkAtoms::superscript));
    1030           0 :     } break;
    1031             :     case roles::MATHML_UNDER:
    1032           0 :       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1033           0 :                              IndexInParent() == 0 ? nsGkAtoms::base :
    1034           0 :                              nsGkAtoms::underscript);
    1035           0 :       break;
    1036             :     case roles::MATHML_OVER:
    1037           0 :       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1038           0 :                              IndexInParent() == 0 ? nsGkAtoms::base :
    1039           0 :                              nsGkAtoms::overscript);
    1040           0 :       break;
    1041             :     case roles::MATHML_UNDER_OVER: {
    1042           0 :       int32_t index = IndexInParent();
    1043           0 :       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1044             :                              index == 0 ? nsGkAtoms::base :
    1045           0 :                              (index == 1 ? nsGkAtoms::underscript :
    1046           0 :                               nsGkAtoms::overscript));
    1047           0 :     } break;
    1048             :     case roles::MATHML_MULTISCRIPTS: {
    1049             :       // Get the <multiscripts> base.
    1050             :       nsIContent* child;
    1051           0 :       bool baseFound = false;
    1052           0 :       for (child = parent->GetContent()->GetFirstChild(); child;
    1053           0 :            child = child->GetNextSibling()) {
    1054           0 :         if (child->IsMathMLElement()) {
    1055           0 :           baseFound = true;
    1056           0 :           break;
    1057             :         }
    1058             :       }
    1059           0 :       if (baseFound) {
    1060           0 :         nsIContent* content = GetContent();
    1061           0 :         if (child == content) {
    1062             :           // We are the base.
    1063             :           nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1064           0 :                                  nsGkAtoms::base);
    1065             :         } else {
    1066             :           // Browse the list of scripts to find us and determine our type.
    1067           0 :           bool postscript = true;
    1068           0 :           bool subscript = true;
    1069           0 :           for (child = child->GetNextSibling(); child;
    1070           0 :                child = child->GetNextSibling()) {
    1071           0 :             if (!child->IsMathMLElement())
    1072           0 :               continue;
    1073           0 :             if (child->IsMathMLElement(nsGkAtoms::mprescripts_)) {
    1074           0 :               postscript = false;
    1075           0 :               subscript = true;
    1076           0 :               continue;
    1077             :             }
    1078           0 :             if (child == content) {
    1079           0 :               if (postscript) {
    1080           0 :                 nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1081             :                                        subscript ?
    1082             :                                        nsGkAtoms::subscript :
    1083           0 :                                        nsGkAtoms::superscript);
    1084             :               } else {
    1085           0 :                 nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::xmlroles,
    1086             :                                        subscript ?
    1087             :                                        nsGkAtoms::presubscript :
    1088           0 :                                        nsGkAtoms::presuperscript);
    1089             :               }
    1090           0 :               break;
    1091             :             }
    1092           0 :             subscript = !subscript;
    1093             :           }
    1094             :         }
    1095             :       }
    1096           0 :     } break;
    1097             :     default:
    1098           0 :       break;
    1099             :     }
    1100             :   }
    1101           0 : }
    1102             : 
    1103             : already_AddRefed<nsIPersistentProperties>
    1104           0 : HyperTextAccessible::NativeAttributes()
    1105             : {
    1106             :   nsCOMPtr<nsIPersistentProperties> attributes =
    1107           0 :     AccessibleWrap::NativeAttributes();
    1108             : 
    1109             :   // 'formatting' attribute is deprecated, 'display' attribute should be
    1110             :   // instead.
    1111           0 :   nsIFrame *frame = GetFrame();
    1112           0 :   if (frame && frame->IsBlockFrame()) {
    1113           0 :     nsAutoString unused;
    1114           0 :     attributes->SetStringProperty(NS_LITERAL_CSTRING("formatting"),
    1115           0 :                                   NS_LITERAL_STRING("block"), unused);
    1116             :   }
    1117             : 
    1118           0 :   if (FocusMgr()->IsFocused(this)) {
    1119           0 :     int32_t lineNumber = CaretLineNumber();
    1120           0 :     if (lineNumber >= 1) {
    1121           0 :       nsAutoString strLineNumber;
    1122           0 :       strLineNumber.AppendInt(lineNumber);
    1123           0 :       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::lineNumber, strLineNumber);
    1124             :     }
    1125             :   }
    1126             : 
    1127           0 :   if (HasOwnContent()) {
    1128           0 :     GetAccService()->MarkupAttributes(mContent, attributes);
    1129           0 :     if (mContent->IsMathMLElement())
    1130           0 :       SetMathMLXMLRoles(attributes);
    1131             :   }
    1132             : 
    1133           0 :   return attributes.forget();
    1134             : }
    1135             : 
    1136             : nsIAtom*
    1137           0 : HyperTextAccessible::LandmarkRole() const
    1138             : {
    1139           0 :   if (!HasOwnContent())
    1140           0 :     return nullptr;
    1141             : 
    1142             :   // For the html landmark elements we expose them like we do ARIA landmarks to
    1143             :   // make AT navigation schemes "just work".
    1144           0 :   if (mContent->IsHTMLElement(nsGkAtoms::nav)) {
    1145           0 :     return nsGkAtoms::navigation;
    1146             :   }
    1147             : 
    1148           0 :   if (mContent->IsAnyOfHTMLElements(nsGkAtoms::header,
    1149             :                                     nsGkAtoms::footer)) {
    1150             :     // Only map header and footer if they are not descendants of an article
    1151             :     // or section tag.
    1152           0 :     nsIContent* parent = mContent->GetParent();
    1153           0 :     while (parent) {
    1154           0 :       if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::section)) {
    1155           0 :         break;
    1156             :       }
    1157           0 :       parent = parent->GetParent();
    1158             :     }
    1159             : 
    1160             :     // No article or section elements found.
    1161           0 :     if (!parent) {
    1162           0 :       if (mContent->IsHTMLElement(nsGkAtoms::header)) {
    1163           0 :         return nsGkAtoms::banner;
    1164             :       }
    1165             : 
    1166           0 :       if (mContent->IsHTMLElement(nsGkAtoms::footer)) {
    1167           0 :         return nsGkAtoms::contentinfo;
    1168             :       }
    1169             :     }
    1170           0 :     return nullptr;
    1171             :   }
    1172             : 
    1173           0 :   if (mContent->IsHTMLElement(nsGkAtoms::aside)) {
    1174           0 :     return nsGkAtoms::complementary;
    1175             :   }
    1176             : 
    1177           0 :   if (mContent->IsHTMLElement(nsGkAtoms::main)) {
    1178           0 :     return nsGkAtoms::main;
    1179             :   }
    1180             : 
    1181           0 :   return nullptr;
    1182             : }
    1183             : 
    1184             : int32_t
    1185           0 : HyperTextAccessible::OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType)
    1186             : {
    1187           0 :   nsIFrame* hyperFrame = GetFrame();
    1188           0 :   if (!hyperFrame)
    1189           0 :     return -1;
    1190             : 
    1191             :   nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType,
    1192           0 :                                                         this);
    1193             : 
    1194           0 :   nsPresContext* presContext = mDoc->PresContext();
    1195             :   nsPoint coordsInAppUnits =
    1196           0 :     ToAppUnits(coords, presContext->AppUnitsPerDevPixel());
    1197             : 
    1198           0 :   nsRect frameScreenRect = hyperFrame->GetScreenRectInAppUnits();
    1199           0 :   if (!frameScreenRect.Contains(coordsInAppUnits.x, coordsInAppUnits.y))
    1200           0 :     return -1; // Not found
    1201             : 
    1202           0 :   nsPoint pointInHyperText(coordsInAppUnits.x - frameScreenRect.x,
    1203           0 :                            coordsInAppUnits.y - frameScreenRect.y);
    1204             : 
    1205             :   // Go through the frames to check if each one has the point.
    1206             :   // When one does, add up the character offsets until we have a match
    1207             : 
    1208             :   // We have an point in an accessible child of this, now we need to add up the
    1209             :   // offsets before it to what we already have
    1210           0 :   int32_t offset = 0;
    1211           0 :   uint32_t childCount = ChildCount();
    1212           0 :   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
    1213           0 :     Accessible* childAcc = mChildren[childIdx];
    1214             : 
    1215           0 :     nsIFrame *primaryFrame = childAcc->GetFrame();
    1216           0 :     NS_ENSURE_TRUE(primaryFrame, -1);
    1217             : 
    1218           0 :     nsIFrame *frame = primaryFrame;
    1219           0 :     while (frame) {
    1220           0 :       nsIContent *content = frame->GetContent();
    1221           0 :       NS_ENSURE_TRUE(content, -1);
    1222           0 :       nsPoint pointInFrame = pointInHyperText - frame->GetOffsetTo(hyperFrame);
    1223           0 :       nsSize frameSize = frame->GetSize();
    1224           0 :       if (pointInFrame.x < frameSize.width && pointInFrame.y < frameSize.height) {
    1225             :         // Finished
    1226           0 :         if (frame->IsTextFrame()) {
    1227             :           nsIFrame::ContentOffsets contentOffsets =
    1228           0 :             frame->GetContentOffsetsFromPointExternal(pointInFrame, nsIFrame::IGNORE_SELECTION_STYLE);
    1229           0 :           if (contentOffsets.IsNull() || contentOffsets.content != content) {
    1230           0 :             return -1; // Not found
    1231             :           }
    1232             :           uint32_t addToOffset;
    1233           0 :           nsresult rv = ContentToRenderedOffset(primaryFrame,
    1234             :                                                 contentOffsets.offset,
    1235           0 :                                                 &addToOffset);
    1236           0 :           NS_ENSURE_SUCCESS(rv, -1);
    1237           0 :           offset += addToOffset;
    1238             :         }
    1239           0 :         return offset;
    1240             :       }
    1241           0 :       frame = frame->GetNextContinuation();
    1242             :     }
    1243             : 
    1244           0 :     offset += nsAccUtils::TextLength(childAcc);
    1245             :   }
    1246             : 
    1247           0 :   return -1; // Not found
    1248             : }
    1249             : 
    1250             : nsIntRect
    1251           0 : HyperTextAccessible::TextBounds(int32_t aStartOffset, int32_t aEndOffset,
    1252             :                                 uint32_t aCoordType)
    1253             : {
    1254           0 :   index_t startOffset = ConvertMagicOffset(aStartOffset);
    1255           0 :   index_t endOffset = ConvertMagicOffset(aEndOffset);
    1256           0 :   if (!startOffset.IsValid() || !endOffset.IsValid() ||
    1257           0 :       startOffset > endOffset || endOffset > CharacterCount()) {
    1258           0 :     NS_ERROR("Wrong in offset");
    1259           0 :     return nsIntRect();
    1260             :   }
    1261             : 
    1262             : 
    1263           0 :   int32_t childIdx = GetChildIndexAtOffset(startOffset);
    1264           0 :   if (childIdx == -1)
    1265           0 :     return nsIntRect();
    1266             : 
    1267           0 :   nsIntRect bounds;
    1268           0 :   int32_t prevOffset = GetChildOffset(childIdx);
    1269           0 :   int32_t offset1 = startOffset - prevOffset;
    1270             : 
    1271           0 :   while (childIdx < static_cast<int32_t>(ChildCount())) {
    1272           0 :     nsIFrame* frame = GetChildAt(childIdx++)->GetFrame();
    1273           0 :     if (!frame) {
    1274           0 :       NS_NOTREACHED("No frame for a child!");
    1275           0 :       continue;
    1276             :     }
    1277             : 
    1278           0 :     int32_t nextOffset = GetChildOffset(childIdx);
    1279           0 :     if (nextOffset >= static_cast<int32_t>(endOffset)) {
    1280           0 :       bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
    1281           0 :                                                 endOffset - prevOffset));
    1282           0 :       break;
    1283             :     }
    1284             : 
    1285           0 :     bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
    1286           0 :                                               nextOffset - prevOffset));
    1287             : 
    1288           0 :     prevOffset = nextOffset;
    1289           0 :     offset1 = 0;
    1290             :   }
    1291             : 
    1292           0 :   nsAccUtils::ConvertScreenCoordsTo(&bounds.x, &bounds.y, aCoordType, this);
    1293           0 :   return bounds;
    1294             : }
    1295             : 
    1296             : already_AddRefed<nsIEditor>
    1297           0 : HyperTextAccessible::GetEditor() const
    1298             : {
    1299           0 :   if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
    1300             :     // If we're inside an editable container, then return that container's editor
    1301           0 :     Accessible* ancestor = Parent();
    1302           0 :     while (ancestor) {
    1303           0 :       HyperTextAccessible* hyperText = ancestor->AsHyperText();
    1304           0 :       if (hyperText) {
    1305             :         // Recursion will stop at container doc because it has its own impl
    1306             :         // of GetEditor()
    1307           0 :         return hyperText->GetEditor();
    1308             :       }
    1309             : 
    1310           0 :       ancestor = ancestor->Parent();
    1311             :     }
    1312             : 
    1313           0 :     return nullptr;
    1314             :   }
    1315             : 
    1316           0 :   nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mContent);
    1317           0 :   nsCOMPtr<nsIEditingSession> editingSession;
    1318           0 :   docShell->GetEditingSession(getter_AddRefs(editingSession));
    1319           0 :   if (!editingSession)
    1320           0 :     return nullptr; // No editing session interface
    1321             : 
    1322           0 :   nsCOMPtr<nsIEditor> editor;
    1323           0 :   nsIDocument* docNode = mDoc->DocumentNode();
    1324           0 :   editingSession->GetEditorForWindow(docNode->GetWindow(),
    1325           0 :                                      getter_AddRefs(editor));
    1326           0 :   return editor.forget();
    1327             : }
    1328             : 
    1329             : /**
    1330             :   * =================== Caret & Selection ======================
    1331             :   */
    1332             : 
    1333             : nsresult
    1334           0 : HyperTextAccessible::SetSelectionRange(int32_t aStartPos, int32_t aEndPos)
    1335             : {
    1336             :   // Before setting the selection range, we need to ensure that the editor
    1337             :   // is initialized. (See bug 804927.)
    1338             :   // Otherwise, it's possible that lazy editor initialization will override
    1339             :   // the selection we set here and leave the caret at the end of the text.
    1340             :   // By calling GetEditor here, we ensure that editor initialization is
    1341             :   // completed before we set the selection.
    1342           0 :   nsCOMPtr<nsIEditor> editor = GetEditor();
    1343             : 
    1344           0 :   bool isFocusable = InteractiveState() & states::FOCUSABLE;
    1345             : 
    1346             :   // If accessible is focusable then focus it before setting the selection to
    1347             :   // neglect control's selection changes on focus if any (for example, inputs
    1348             :   // that do select all on focus).
    1349             :   // some input controls
    1350           0 :   if (isFocusable)
    1351           0 :     TakeFocus();
    1352             : 
    1353           0 :   dom::Selection* domSel = DOMSelection();
    1354           0 :   NS_ENSURE_STATE(domSel);
    1355             : 
    1356             :   // Set up the selection.
    1357           0 :   for (int32_t idx = domSel->RangeCount() - 1; idx > 0; idx--)
    1358           0 :     domSel->RemoveRange(domSel->GetRangeAt(idx));
    1359           0 :   SetSelectionBoundsAt(0, aStartPos, aEndPos);
    1360             : 
    1361             :   // When selection is done, move the focus to the selection if accessible is
    1362             :   // not focusable. That happens when selection is set within hypertext
    1363             :   // accessible.
    1364           0 :   if (isFocusable)
    1365           0 :     return NS_OK;
    1366             : 
    1367           0 :   nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
    1368           0 :   if (DOMFocusManager) {
    1369           0 :     NS_ENSURE_TRUE(mDoc, NS_ERROR_FAILURE);
    1370           0 :     nsIDocument* docNode = mDoc->DocumentNode();
    1371           0 :     NS_ENSURE_TRUE(docNode, NS_ERROR_FAILURE);
    1372           0 :     nsCOMPtr<nsPIDOMWindowOuter> window = docNode->GetWindow();
    1373           0 :     nsCOMPtr<nsIDOMElement> result;
    1374           0 :     DOMFocusManager->MoveFocus(window, nullptr, nsIFocusManager::MOVEFOCUS_CARET,
    1375           0 :                                nsIFocusManager::FLAG_BYMOVEFOCUS, getter_AddRefs(result));
    1376             :   }
    1377             : 
    1378           0 :   return NS_OK;
    1379             : }
    1380             : 
    1381             : int32_t
    1382           0 : HyperTextAccessible::CaretOffset() const
    1383             : {
    1384             :   // Not focused focusable accessible except document accessible doesn't have
    1385             :   // a caret.
    1386           0 :   if (!IsDoc() && !FocusMgr()->IsFocused(this) &&
    1387           0 :       (InteractiveState() & states::FOCUSABLE)) {
    1388           0 :     return -1;
    1389             :   }
    1390             : 
    1391             :   // Check cached value.
    1392           0 :   int32_t caretOffset = -1;
    1393           0 :   HyperTextAccessible* text = SelectionMgr()->AccessibleWithCaret(&caretOffset);
    1394             : 
    1395             :   // Use cached value if it corresponds to this accessible.
    1396           0 :   if (caretOffset != -1) {
    1397           0 :     if (text == this)
    1398           0 :       return caretOffset;
    1399             : 
    1400           0 :     nsINode* textNode = text->GetNode();
    1401             :     // Ignore offset if cached accessible isn't a text leaf.
    1402           0 :     if (nsCoreUtils::IsAncestorOf(GetNode(), textNode))
    1403           0 :       return TransformOffset(text,
    1404           0 :         textNode->IsNodeOfType(nsINode::eTEXT) ? caretOffset : 0, false);
    1405             :   }
    1406             : 
    1407             :   // No caret if the focused node is not inside this DOM node and this DOM node
    1408             :   // is not inside of focused node.
    1409             :   FocusManager::FocusDisposition focusDisp =
    1410           0 :     FocusMgr()->IsInOrContainsFocus(this);
    1411           0 :   if (focusDisp == FocusManager::eNone)
    1412           0 :     return -1;
    1413             : 
    1414             :   // Turn the focus node and offset of the selection into caret hypretext
    1415             :   // offset.
    1416           0 :   dom::Selection* domSel = DOMSelection();
    1417           0 :   NS_ENSURE_TRUE(domSel, -1);
    1418             : 
    1419           0 :   nsINode* focusNode = domSel->GetFocusNode();
    1420           0 :   uint32_t focusOffset = domSel->FocusOffset();
    1421             : 
    1422             :   // No caret if this DOM node is inside of focused node but the selection's
    1423             :   // focus point is not inside of this DOM node.
    1424           0 :   if (focusDisp == FocusManager::eContainedByFocus) {
    1425             :     nsINode* resultNode =
    1426           0 :       nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
    1427             : 
    1428           0 :     nsINode* thisNode = GetNode();
    1429           0 :     if (resultNode != thisNode &&
    1430           0 :         !nsCoreUtils::IsAncestorOf(thisNode, resultNode))
    1431           0 :       return -1;
    1432             :   }
    1433             : 
    1434           0 :   return DOMPointToOffset(focusNode, focusOffset);
    1435             : }
    1436             : 
    1437             : int32_t
    1438           0 : HyperTextAccessible::CaretLineNumber()
    1439             : {
    1440             :   // Provide the line number for the caret, relative to the
    1441             :   // currently focused node. Use a 1-based index
    1442           0 :   RefPtr<nsFrameSelection> frameSelection = FrameSelection();
    1443           0 :   if (!frameSelection)
    1444           0 :     return -1;
    1445             : 
    1446           0 :   dom::Selection* domSel = frameSelection->GetSelection(SelectionType::eNormal);
    1447           0 :   if (!domSel)
    1448           0 :     return - 1;
    1449             : 
    1450           0 :   nsINode* caretNode = domSel->GetFocusNode();
    1451           0 :   if (!caretNode || !caretNode->IsContent())
    1452           0 :     return -1;
    1453             : 
    1454           0 :   nsIContent* caretContent = caretNode->AsContent();
    1455           0 :   if (!nsCoreUtils::IsAncestorOf(GetNode(), caretContent))
    1456           0 :     return -1;
    1457             : 
    1458             :   int32_t returnOffsetUnused;
    1459           0 :   uint32_t caretOffset = domSel->FocusOffset();
    1460           0 :   CaretAssociationHint hint = frameSelection->GetHint();
    1461           0 :   nsIFrame *caretFrame = frameSelection->GetFrameForNodeOffset(caretContent, caretOffset,
    1462           0 :                                                                hint, &returnOffsetUnused);
    1463           0 :   NS_ENSURE_TRUE(caretFrame, -1);
    1464             : 
    1465           0 :   int32_t lineNumber = 1;
    1466           0 :   nsAutoLineIterator lineIterForCaret;
    1467           0 :   nsIContent *hyperTextContent = IsContent() ? mContent.get() : nullptr;
    1468           0 :   while (caretFrame) {
    1469           0 :     if (hyperTextContent == caretFrame->GetContent()) {
    1470           0 :       return lineNumber; // Must be in a single line hyper text, there is no line iterator
    1471             :     }
    1472           0 :     nsContainerFrame *parentFrame = caretFrame->GetParent();
    1473           0 :     if (!parentFrame)
    1474           0 :       break;
    1475             : 
    1476             :     // Add lines for the sibling frames before the caret
    1477           0 :     nsIFrame *sibling = parentFrame->PrincipalChildList().FirstChild();
    1478           0 :     while (sibling && sibling != caretFrame) {
    1479           0 :       nsAutoLineIterator lineIterForSibling = sibling->GetLineIterator();
    1480           0 :       if (lineIterForSibling) {
    1481             :         // For the frames before that grab all the lines
    1482           0 :         int32_t addLines = lineIterForSibling->GetNumLines();
    1483           0 :         lineNumber += addLines;
    1484             :       }
    1485           0 :       sibling = sibling->GetNextSibling();
    1486             :     }
    1487             : 
    1488             :     // Get the line number relative to the container with lines
    1489           0 :     if (!lineIterForCaret) {   // Add the caret line just once
    1490           0 :       lineIterForCaret = parentFrame->GetLineIterator();
    1491           0 :       if (lineIterForCaret) {
    1492             :         // Ancestor of caret
    1493           0 :         int32_t addLines = lineIterForCaret->FindLineContaining(caretFrame);
    1494           0 :         lineNumber += addLines;
    1495             :       }
    1496             :     }
    1497             : 
    1498           0 :     caretFrame = parentFrame;
    1499             :   }
    1500             : 
    1501           0 :   NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
    1502           0 :   return lineNumber;
    1503             : }
    1504             : 
    1505             : LayoutDeviceIntRect
    1506           0 : HyperTextAccessible::GetCaretRect(nsIWidget** aWidget)
    1507             : {
    1508           0 :   *aWidget = nullptr;
    1509             : 
    1510           0 :   RefPtr<nsCaret> caret = mDoc->PresShell()->GetCaret();
    1511           0 :   NS_ENSURE_TRUE(caret, LayoutDeviceIntRect());
    1512             : 
    1513           0 :   bool isVisible = caret->IsVisible();
    1514           0 :   if (!isVisible)
    1515           0 :     return LayoutDeviceIntRect();
    1516             : 
    1517           0 :   nsRect rect;
    1518           0 :   nsIFrame* frame = caret->GetGeometry(&rect);
    1519           0 :   if (!frame || rect.IsEmpty())
    1520           0 :     return LayoutDeviceIntRect();
    1521             : 
    1522           0 :   nsPoint offset;
    1523             :   // Offset from widget origin to the frame origin, which includes chrome
    1524             :   // on the widget.
    1525           0 :   *aWidget = frame->GetNearestWidget(offset);
    1526           0 :   NS_ENSURE_TRUE(*aWidget, LayoutDeviceIntRect());
    1527           0 :   rect.MoveBy(offset);
    1528             : 
    1529             :   LayoutDeviceIntRect caretRect = LayoutDeviceIntRect::FromUnknownRect(
    1530           0 :     rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel()));
    1531             :   // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
    1532           0 :   caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
    1533             : 
    1534             :   // Correct for character size, so that caret always matches the size of
    1535             :   // the character. This is important for font size transitions, and is
    1536             :   // necessary because the Gecko caret uses the previous character's size as
    1537             :   // the user moves forward in the text by character.
    1538             :   nsIntRect charRect = CharBounds(CaretOffset(),
    1539           0 :                                   nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
    1540           0 :   if (!charRect.IsEmpty()) {
    1541           0 :     caretRect.height -= charRect.y - caretRect.y;
    1542           0 :     caretRect.y = charRect.y;
    1543             :   }
    1544           0 :   return caretRect;
    1545             : }
    1546             : 
    1547             : void
    1548           0 : HyperTextAccessible::GetSelectionDOMRanges(SelectionType aSelectionType,
    1549             :                                            nsTArray<nsRange*>* aRanges)
    1550             : {
    1551             :   // Ignore selection if it is not visible.
    1552           0 :   RefPtr<nsFrameSelection> frameSelection = FrameSelection();
    1553           0 :   if (!frameSelection ||
    1554           0 :       frameSelection->GetDisplaySelection() <= nsISelectionController::SELECTION_HIDDEN)
    1555           0 :     return;
    1556             : 
    1557           0 :   dom::Selection* domSel = frameSelection->GetSelection(aSelectionType);
    1558           0 :   if (!domSel)
    1559           0 :     return;
    1560             : 
    1561           0 :   nsCOMPtr<nsINode> startNode = GetNode();
    1562             : 
    1563           0 :   nsCOMPtr<nsIEditor> editor = GetEditor();
    1564           0 :   if (editor) {
    1565           0 :     nsCOMPtr<nsIDOMElement> editorRoot;
    1566           0 :     editor->GetRootElement(getter_AddRefs(editorRoot));
    1567           0 :     startNode = do_QueryInterface(editorRoot);
    1568             :   }
    1569             : 
    1570           0 :   if (!startNode)
    1571           0 :     return;
    1572             : 
    1573           0 :   uint32_t childCount = startNode->GetChildCount();
    1574             :   nsresult rv = domSel->
    1575           0 :     GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
    1576           0 :   NS_ENSURE_SUCCESS_VOID(rv);
    1577             : 
    1578             :   // Remove collapsed ranges
    1579           0 :   uint32_t numRanges = aRanges->Length();
    1580           0 :   for (uint32_t idx = 0; idx < numRanges; idx ++) {
    1581           0 :     if ((*aRanges)[idx]->Collapsed()) {
    1582           0 :       aRanges->RemoveElementAt(idx);
    1583           0 :       --numRanges;
    1584           0 :       --idx;
    1585             :     }
    1586             :   }
    1587             : }
    1588             : 
    1589             : int32_t
    1590           0 : HyperTextAccessible::SelectionCount()
    1591             : {
    1592           0 :   nsTArray<nsRange*> ranges;
    1593           0 :   GetSelectionDOMRanges(SelectionType::eNormal, &ranges);
    1594           0 :   return ranges.Length();
    1595             : }
    1596             : 
    1597             : bool
    1598           0 : HyperTextAccessible::SelectionBoundsAt(int32_t aSelectionNum,
    1599             :                                        int32_t* aStartOffset,
    1600             :                                        int32_t* aEndOffset)
    1601             : {
    1602           0 :   *aStartOffset = *aEndOffset = 0;
    1603             : 
    1604           0 :   nsTArray<nsRange*> ranges;
    1605           0 :   GetSelectionDOMRanges(SelectionType::eNormal, &ranges);
    1606             : 
    1607           0 :   uint32_t rangeCount = ranges.Length();
    1608           0 :   if (aSelectionNum < 0 || aSelectionNum >= static_cast<int32_t>(rangeCount))
    1609           0 :     return false;
    1610             : 
    1611           0 :   nsRange* range = ranges[aSelectionNum];
    1612             : 
    1613             :   // Get start and end points.
    1614           0 :   nsINode* startNode = range->GetStartContainer();
    1615           0 :   nsINode* endNode = range->GetEndContainer();
    1616           0 :   int32_t startOffset = range->StartOffset(), endOffset = range->EndOffset();
    1617             : 
    1618             :   // Make sure start is before end, by swapping DOM points.  This occurs when
    1619             :   // the user selects backwards in the text.
    1620             :   int32_t rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
    1621           0 :                                                        startNode, startOffset);
    1622           0 :   if (rangeCompare < 0) {
    1623           0 :     nsINode* tempNode = startNode;
    1624           0 :     startNode = endNode;
    1625           0 :     endNode = tempNode;
    1626           0 :     int32_t tempOffset = startOffset;
    1627           0 :     startOffset = endOffset;
    1628           0 :     endOffset = tempOffset;
    1629             :   }
    1630             : 
    1631           0 :   if (!nsContentUtils::ContentIsDescendantOf(startNode, mContent))
    1632           0 :     *aStartOffset = 0;
    1633             :   else
    1634           0 :     *aStartOffset = DOMPointToOffset(startNode, startOffset);
    1635             : 
    1636           0 :   if (!nsContentUtils::ContentIsDescendantOf(endNode, mContent))
    1637           0 :     *aEndOffset = CharacterCount();
    1638             :   else
    1639           0 :     *aEndOffset = DOMPointToOffset(endNode, endOffset, true);
    1640           0 :   return true;
    1641             : }
    1642             : 
    1643             : bool
    1644           0 : HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
    1645             :                                           int32_t aStartOffset,
    1646             :                                           int32_t aEndOffset)
    1647             : {
    1648           0 :   index_t startOffset = ConvertMagicOffset(aStartOffset);
    1649           0 :   index_t endOffset = ConvertMagicOffset(aEndOffset);
    1650           0 :   if (!startOffset.IsValid() || !endOffset.IsValid() ||
    1651           0 :       startOffset > endOffset || endOffset > CharacterCount()) {
    1652           0 :     NS_ERROR("Wrong in offset");
    1653           0 :     return false;
    1654             :   }
    1655             : 
    1656           0 :   dom::Selection* domSel = DOMSelection();
    1657           0 :   if (!domSel)
    1658           0 :     return false;
    1659             : 
    1660           0 :   RefPtr<nsRange> range;
    1661           0 :   uint32_t rangeCount = domSel->RangeCount();
    1662           0 :   if (aSelectionNum == static_cast<int32_t>(rangeCount))
    1663           0 :     range = new nsRange(mContent);
    1664             :   else
    1665           0 :     range = domSel->GetRangeAt(aSelectionNum);
    1666             : 
    1667           0 :   if (!range)
    1668           0 :     return false;
    1669             : 
    1670           0 :   if (!OffsetsToDOMRange(startOffset, endOffset, range))
    1671           0 :     return false;
    1672             : 
    1673             :   // If new range was created then add it, otherwise notify selection listeners
    1674             :   // that existing selection range was changed.
    1675           0 :   if (aSelectionNum == static_cast<int32_t>(rangeCount))
    1676           0 :     return NS_SUCCEEDED(domSel->AddRange(range));
    1677             : 
    1678           0 :   domSel->RemoveRange(range);
    1679           0 :   return NS_SUCCEEDED(domSel->AddRange(range));
    1680             : }
    1681             : 
    1682             : bool
    1683           0 : HyperTextAccessible::RemoveFromSelection(int32_t aSelectionNum)
    1684             : {
    1685           0 :   dom::Selection* domSel = DOMSelection();
    1686           0 :   if (!domSel)
    1687           0 :     return false;
    1688             : 
    1689           0 :   if (aSelectionNum < 0 || aSelectionNum >= static_cast<int32_t>(domSel->RangeCount()))
    1690           0 :     return false;
    1691             : 
    1692           0 :   domSel->RemoveRange(domSel->GetRangeAt(aSelectionNum));
    1693           0 :   return true;
    1694             : }
    1695             : 
    1696             : void
    1697           0 : HyperTextAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
    1698             :                                        uint32_t aScrollType)
    1699             : {
    1700           0 :   RefPtr<nsRange> range = new nsRange(mContent);
    1701           0 :   if (OffsetsToDOMRange(aStartOffset, aEndOffset, range))
    1702           0 :     nsCoreUtils::ScrollSubstringTo(GetFrame(), range, aScrollType);
    1703           0 : }
    1704             : 
    1705             : void
    1706           0 : HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
    1707             :                                             int32_t aEndOffset,
    1708             :                                             uint32_t aCoordinateType,
    1709             :                                             int32_t aX, int32_t aY)
    1710             : {
    1711           0 :   nsIFrame *frame = GetFrame();
    1712           0 :   if (!frame)
    1713           0 :     return;
    1714             : 
    1715             :   nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
    1716           0 :                                                         this);
    1717             : 
    1718           0 :   RefPtr<nsRange> range = new nsRange(mContent);
    1719           0 :   if (!OffsetsToDOMRange(aStartOffset, aEndOffset, range))
    1720           0 :     return;
    1721             : 
    1722           0 :   nsPresContext* presContext = frame->PresContext();
    1723             :   nsPoint coordsInAppUnits =
    1724           0 :     ToAppUnits(coords, presContext->AppUnitsPerDevPixel());
    1725             : 
    1726           0 :   bool initialScrolled = false;
    1727           0 :   nsIFrame *parentFrame = frame;
    1728           0 :   while ((parentFrame = parentFrame->GetParent())) {
    1729           0 :     nsIScrollableFrame *scrollableFrame = do_QueryFrame(parentFrame);
    1730           0 :     if (scrollableFrame) {
    1731           0 :       if (!initialScrolled) {
    1732             :         // Scroll substring to the given point. Turn the point into percents
    1733             :         // relative scrollable area to use nsCoreUtils::ScrollSubstringTo.
    1734           0 :         nsRect frameRect = parentFrame->GetScreenRectInAppUnits();
    1735           0 :         nscoord offsetPointX = coordsInAppUnits.x - frameRect.x;
    1736           0 :         nscoord offsetPointY = coordsInAppUnits.y - frameRect.y;
    1737             : 
    1738           0 :         nsSize size(parentFrame->GetSize());
    1739             : 
    1740             :         // avoid divide by zero
    1741           0 :         size.width = size.width ? size.width : 1;
    1742           0 :         size.height = size.height ? size.height : 1;
    1743             : 
    1744           0 :         int16_t hPercent = offsetPointX * 100 / size.width;
    1745           0 :         int16_t vPercent = offsetPointY * 100 / size.height;
    1746             : 
    1747           0 :         nsresult rv = nsCoreUtils::ScrollSubstringTo(frame, range,
    1748             :                                                      nsIPresShell::ScrollAxis(vPercent),
    1749           0 :                                                      nsIPresShell::ScrollAxis(hPercent));
    1750           0 :         if (NS_FAILED(rv))
    1751           0 :           return;
    1752             : 
    1753           0 :         initialScrolled = true;
    1754             :       } else {
    1755             :         // Substring was scrolled to the given point already inside its closest
    1756             :         // scrollable area. If there are nested scrollable areas then make
    1757             :         // sure we scroll lower areas to the given point inside currently
    1758             :         // traversed scrollable area.
    1759           0 :         nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
    1760             :       }
    1761             :     }
    1762           0 :     frame = parentFrame;
    1763             :   }
    1764             : }
    1765             : 
    1766             : void
    1767           0 : HyperTextAccessible::EnclosingRange(a11y::TextRange& aRange) const
    1768             : {
    1769           0 :   if (IsTextField()) {
    1770           0 :     aRange.Set(mDoc, const_cast<HyperTextAccessible*>(this), 0,
    1771           0 :                const_cast<HyperTextAccessible*>(this), CharacterCount());
    1772             :   } else {
    1773           0 :     aRange.Set(mDoc, mDoc, 0, mDoc, mDoc->CharacterCount());
    1774             :   }
    1775           0 : }
    1776             : 
    1777             : void
    1778           0 : HyperTextAccessible::SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const
    1779             : {
    1780           0 :   MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty");
    1781             : 
    1782           0 :   dom::Selection* sel = DOMSelection();
    1783           0 :   if (!sel)
    1784           0 :     return;
    1785             : 
    1786           0 :   aRanges->SetCapacity(sel->RangeCount());
    1787             : 
    1788           0 :   for (uint32_t idx = 0; idx < sel->RangeCount(); idx++) {
    1789           0 :     nsRange* DOMRange = sel->GetRangeAt(idx);
    1790             :     HyperTextAccessible* startContainer =
    1791           0 :       nsAccUtils::GetTextContainer(DOMRange->GetStartContainer());
    1792             :     HyperTextAccessible* endContainer =
    1793           0 :       nsAccUtils::GetTextContainer(DOMRange->GetEndContainer());
    1794           0 :     if (!startContainer || !endContainer) {
    1795           0 :       continue;
    1796             :     }
    1797             : 
    1798             :     int32_t startOffset =
    1799           0 :       startContainer->DOMPointToOffset(DOMRange->GetStartContainer(),
    1800           0 :                                        DOMRange->StartOffset(), false);
    1801             :     int32_t endOffset =
    1802           0 :       endContainer->DOMPointToOffset(DOMRange->GetEndContainer(),
    1803           0 :                                      DOMRange->EndOffset(), true);
    1804             : 
    1805           0 :     TextRange tr(IsTextField() ? const_cast<HyperTextAccessible*>(this) : mDoc,
    1806           0 :                     startContainer, startOffset, endContainer, endOffset);
    1807           0 :     *(aRanges->AppendElement()) = Move(tr);
    1808             :   }
    1809             : }
    1810             : 
    1811             : void
    1812           0 : HyperTextAccessible::VisibleRanges(nsTArray<a11y::TextRange>* aRanges) const
    1813             : {
    1814           0 : }
    1815             : 
    1816             : void
    1817           0 : HyperTextAccessible::RangeByChild(Accessible* aChild,
    1818             :                                   a11y::TextRange& aRange) const
    1819             : {
    1820           0 :   HyperTextAccessible* ht = aChild->AsHyperText();
    1821           0 :   if (ht) {
    1822           0 :     aRange.Set(mDoc, ht, 0, ht, ht->CharacterCount());
    1823           0 :     return;
    1824             :   }
    1825             : 
    1826           0 :   Accessible* child = aChild;
    1827           0 :   Accessible* parent = nullptr;
    1828           0 :   while ((parent = child->Parent()) && !(ht = parent->AsHyperText()))
    1829           0 :     child = parent;
    1830             : 
    1831             :   // If no text then return collapsed text range, otherwise return a range
    1832             :   // containing the text enclosed by the given child.
    1833           0 :   if (ht) {
    1834           0 :     int32_t childIdx = child->IndexInParent();
    1835           0 :     int32_t startOffset = ht->GetChildOffset(childIdx);
    1836           0 :     int32_t endOffset = child->IsTextLeaf() ?
    1837           0 :       ht->GetChildOffset(childIdx + 1) : startOffset;
    1838           0 :     aRange.Set(mDoc, ht, startOffset, ht, endOffset);
    1839             :   }
    1840             : }
    1841             : 
    1842             : void
    1843           0 : HyperTextAccessible::RangeAtPoint(int32_t aX, int32_t aY,
    1844             :                                   a11y::TextRange& aRange) const
    1845             : {
    1846           0 :   Accessible* child = mDoc->ChildAtPoint(aX, aY, eDeepestChild);
    1847           0 :   if (!child)
    1848           0 :     return;
    1849             : 
    1850           0 :   Accessible* parent = nullptr;
    1851           0 :   while ((parent = child->Parent()) && !parent->IsHyperText())
    1852           0 :     child = parent;
    1853             : 
    1854             :   // Return collapsed text range for the point.
    1855           0 :   if (parent) {
    1856           0 :     HyperTextAccessible* ht = parent->AsHyperText();
    1857           0 :     int32_t offset = ht->GetChildOffset(child);
    1858           0 :     aRange.Set(mDoc, ht, offset, ht, offset);
    1859             :   }
    1860             : }
    1861             : 
    1862             : ////////////////////////////////////////////////////////////////////////////////
    1863             : // Accessible public
    1864             : 
    1865             : // Accessible protected
    1866             : ENameValueFlag
    1867           0 : HyperTextAccessible::NativeName(nsString& aName)
    1868             : {
    1869             :   // Check @alt attribute for invalid img elements.
    1870           0 :   bool hasImgAlt = false;
    1871           0 :   if (mContent->IsHTMLElement(nsGkAtoms::img)) {
    1872           0 :     hasImgAlt = mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName);
    1873           0 :     if (!aName.IsEmpty())
    1874           0 :       return eNameOK;
    1875             :   }
    1876             : 
    1877           0 :   ENameValueFlag nameFlag = AccessibleWrap::NativeName(aName);
    1878           0 :   if (!aName.IsEmpty())
    1879           0 :     return nameFlag;
    1880             : 
    1881             :   // Get name from title attribute for HTML abbr and acronym elements making it
    1882             :   // a valid name from markup. Otherwise their name isn't picked up by recursive
    1883             :   // name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP.
    1884           0 :   if (IsAbbreviation() &&
    1885           0 :       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName))
    1886           0 :     aName.CompressWhitespace();
    1887             : 
    1888           0 :   return hasImgAlt ? eNoNameOnPurpose : eNameOK;
    1889             : }
    1890             : 
    1891             : void
    1892           0 : HyperTextAccessible::Shutdown()
    1893             : {
    1894           0 :   mOffsets.Clear();
    1895           0 :   AccessibleWrap::Shutdown();
    1896           0 : }
    1897             : 
    1898             : bool
    1899           0 : HyperTextAccessible::RemoveChild(Accessible* aAccessible)
    1900             : {
    1901           0 :   int32_t childIndex = aAccessible->IndexInParent();
    1902           0 :   int32_t count = mOffsets.Length() - childIndex;
    1903           0 :   if (count > 0)
    1904           0 :     mOffsets.RemoveElementsAt(childIndex, count);
    1905             : 
    1906           0 :   return AccessibleWrap::RemoveChild(aAccessible);
    1907             : }
    1908             : 
    1909             : bool
    1910           0 : HyperTextAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
    1911             : {
    1912           0 :   int32_t count = mOffsets.Length() - aIndex;
    1913           0 :   if (count > 0 ) {
    1914           0 :     mOffsets.RemoveElementsAt(aIndex, count);
    1915             :   }
    1916           0 :   return AccessibleWrap::InsertChildAt(aIndex, aChild);
    1917             : }
    1918             : 
    1919             : Relation
    1920           0 : HyperTextAccessible::RelationByType(RelationType aType)
    1921             : {
    1922           0 :   Relation rel = Accessible::RelationByType(aType);
    1923             : 
    1924           0 :   switch (aType) {
    1925             :     case RelationType::NODE_CHILD_OF:
    1926           0 :       if (HasOwnContent() && mContent->IsMathMLElement()) {
    1927           0 :         Accessible* parent = Parent();
    1928           0 :         if (parent) {
    1929           0 :           nsIContent* parentContent = parent->GetContent();
    1930           0 :           if (parentContent &&
    1931           0 :               parentContent->IsMathMLElement(nsGkAtoms::mroot_)) {
    1932             :             // Add a relation pointing to the parent <mroot>.
    1933           0 :             rel.AppendTarget(parent);
    1934             :           }
    1935             :         }
    1936             :       }
    1937           0 :       break;
    1938             :     case RelationType::NODE_PARENT_OF:
    1939           0 :       if (HasOwnContent() && mContent->IsMathMLElement(nsGkAtoms::mroot_)) {
    1940           0 :         Accessible* base = GetChildAt(0);
    1941           0 :         Accessible* index = GetChildAt(1);
    1942           0 :         if (base && index) {
    1943             :           // Append the <mroot> children in the order index, base.
    1944           0 :           rel.AppendTarget(index);
    1945           0 :           rel.AppendTarget(base);
    1946             :         }
    1947             :       }
    1948           0 :       break;
    1949             :     default:
    1950           0 :       break;
    1951             :   }
    1952             : 
    1953           0 :   return rel;
    1954             : }
    1955             : 
    1956             : ////////////////////////////////////////////////////////////////////////////////
    1957             : // HyperTextAccessible public static
    1958             : 
    1959             : nsresult
    1960           0 : HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentOffset,
    1961             :                                              uint32_t* aRenderedOffset) const
    1962             : {
    1963           0 :   if (!aFrame) {
    1964             :     // Current frame not rendered -- this can happen if text is set on
    1965             :     // something with display: none
    1966           0 :     *aRenderedOffset = 0;
    1967           0 :     return NS_OK;
    1968             :   }
    1969             : 
    1970           0 :   if (IsTextField()) {
    1971           0 :     *aRenderedOffset = aContentOffset;
    1972           0 :     return NS_OK;
    1973             :   }
    1974             : 
    1975           0 :   NS_ASSERTION(aFrame->IsTextFrame(), "Need text frame for offset conversion");
    1976           0 :   NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
    1977             :                "Call on primary frame only");
    1978             : 
    1979             :   nsIFrame::RenderedText text = aFrame->GetRenderedText(aContentOffset,
    1980           0 :       aContentOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
    1981           0 :       nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
    1982           0 :   *aRenderedOffset = text.mOffsetWithinNodeRenderedText;
    1983             : 
    1984           0 :   return NS_OK;
    1985             : }
    1986             : 
    1987             : nsresult
    1988           0 : HyperTextAccessible::RenderedToContentOffset(nsIFrame* aFrame, uint32_t aRenderedOffset,
    1989             :                                              int32_t* aContentOffset) const
    1990             : {
    1991           0 :   if (IsTextField()) {
    1992           0 :     *aContentOffset = aRenderedOffset;
    1993           0 :     return NS_OK;
    1994             :   }
    1995             : 
    1996           0 :   *aContentOffset = 0;
    1997           0 :   NS_ENSURE_TRUE(aFrame, NS_ERROR_FAILURE);
    1998             : 
    1999           0 :   NS_ASSERTION(aFrame->IsTextFrame(), "Need text frame for offset conversion");
    2000           0 :   NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
    2001             :                "Call on primary frame only");
    2002             : 
    2003             :   nsIFrame::RenderedText text = aFrame->GetRenderedText(aRenderedOffset,
    2004             :       aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT,
    2005           0 :       nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
    2006           0 :   *aContentOffset = text.mOffsetWithinNodeText;
    2007             : 
    2008           0 :   return NS_OK;
    2009             : }
    2010             : 
    2011             : ////////////////////////////////////////////////////////////////////////////////
    2012             : // HyperTextAccessible public
    2013             : 
    2014             : int32_t
    2015           0 : HyperTextAccessible::GetChildOffset(uint32_t aChildIndex,
    2016             :                                     bool aInvalidateAfter) const
    2017             : {
    2018           0 :   if (aChildIndex == 0) {
    2019           0 :     if (aInvalidateAfter)
    2020           0 :       mOffsets.Clear();
    2021             : 
    2022           0 :     return aChildIndex;
    2023             :   }
    2024             : 
    2025           0 :   int32_t count = mOffsets.Length() - aChildIndex;
    2026           0 :   if (count > 0) {
    2027           0 :     if (aInvalidateAfter)
    2028           0 :       mOffsets.RemoveElementsAt(aChildIndex, count);
    2029             : 
    2030           0 :     return mOffsets[aChildIndex - 1];
    2031             :   }
    2032             : 
    2033           0 :   uint32_t lastOffset = mOffsets.IsEmpty() ?
    2034           0 :     0 : mOffsets[mOffsets.Length() - 1];
    2035             : 
    2036           0 :   while (mOffsets.Length() < aChildIndex) {
    2037           0 :     Accessible* child = mChildren[mOffsets.Length()];
    2038           0 :     lastOffset += nsAccUtils::TextLength(child);
    2039           0 :     mOffsets.AppendElement(lastOffset);
    2040             :   }
    2041             : 
    2042           0 :   return mOffsets[aChildIndex - 1];
    2043             : }
    2044             : 
    2045             : int32_t
    2046           0 : HyperTextAccessible::GetChildIndexAtOffset(uint32_t aOffset) const
    2047             : {
    2048           0 :   uint32_t lastOffset = 0;
    2049           0 :   const uint32_t offsetCount = mOffsets.Length();
    2050             : 
    2051           0 :   if (offsetCount > 0) {
    2052           0 :     lastOffset = mOffsets[offsetCount - 1];
    2053           0 :     if (aOffset < lastOffset) {
    2054             :       size_t index;
    2055           0 :       if (BinarySearch(mOffsets, 0, offsetCount, aOffset, &index)) {
    2056           0 :         return (index < (offsetCount - 1)) ? index + 1 : index;
    2057             :       }
    2058             : 
    2059           0 :       return (index == offsetCount) ? -1 : index;
    2060             :     }
    2061             :   }
    2062             : 
    2063           0 :   uint32_t childCount = ChildCount();
    2064           0 :   while (mOffsets.Length() < childCount) {
    2065           0 :     Accessible* child = GetChildAt(mOffsets.Length());
    2066           0 :     lastOffset += nsAccUtils::TextLength(child);
    2067           0 :     mOffsets.AppendElement(lastOffset);
    2068           0 :     if (aOffset < lastOffset)
    2069           0 :       return mOffsets.Length() - 1;
    2070             :   }
    2071             : 
    2072           0 :   if (aOffset == lastOffset)
    2073           0 :     return mOffsets.Length() - 1;
    2074             : 
    2075           0 :   return -1;
    2076             : }
    2077             : 
    2078             : ////////////////////////////////////////////////////////////////////////////////
    2079             : // HyperTextAccessible protected
    2080             : 
    2081             : nsresult
    2082           0 : HyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame* aFrame, int32_t aOffset,
    2083             :                                               Accessible* aAccessible,
    2084             :                                               DOMPoint* aPoint)
    2085             : {
    2086           0 :   NS_ENSURE_ARG(aAccessible);
    2087             : 
    2088           0 :   if (!aFrame) {
    2089             :     // If the given frame is null then set offset after the DOM node of the
    2090             :     // given accessible.
    2091           0 :     NS_ASSERTION(!aAccessible->IsDoc(),
    2092             :                  "Shouldn't be called on document accessible!");
    2093             : 
    2094           0 :     nsIContent* content = aAccessible->GetContent();
    2095           0 :     NS_ASSERTION(content, "Shouldn't operate on defunct accessible!");
    2096             : 
    2097           0 :     nsIContent* parent = content->GetParent();
    2098             : 
    2099           0 :     aPoint->idx = parent->IndexOf(content) + 1;
    2100           0 :     aPoint->node = parent;
    2101             : 
    2102           0 :   } else if (aFrame->IsTextFrame()) {
    2103           0 :     nsIContent* content = aFrame->GetContent();
    2104           0 :     NS_ENSURE_STATE(content);
    2105             : 
    2106           0 :     nsIFrame *primaryFrame = content->GetPrimaryFrame();
    2107           0 :     nsresult rv = RenderedToContentOffset(primaryFrame, aOffset, &(aPoint->idx));
    2108           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2109             : 
    2110           0 :     aPoint->node = content;
    2111             : 
    2112             :   } else {
    2113           0 :     nsIContent* content = aFrame->GetContent();
    2114           0 :     NS_ENSURE_STATE(content);
    2115             : 
    2116           0 :     nsIContent* parent = content->GetParent();
    2117           0 :     NS_ENSURE_STATE(parent);
    2118             : 
    2119           0 :     aPoint->idx = parent->IndexOf(content);
    2120           0 :     aPoint->node = parent;
    2121             :   }
    2122             : 
    2123           0 :   return NS_OK;
    2124             : }
    2125             : 
    2126             : // HyperTextAccessible
    2127             : void
    2128           0 : HyperTextAccessible::GetSpellTextAttr(nsINode* aNode,
    2129             :                                       int32_t aNodeOffset,
    2130             :                                       uint32_t* aStartOffset,
    2131             :                                       uint32_t* aEndOffset,
    2132             :                                       nsIPersistentProperties* aAttributes)
    2133             : {
    2134           0 :   RefPtr<nsFrameSelection> fs = FrameSelection();
    2135           0 :   if (!fs)
    2136           0 :     return;
    2137             : 
    2138           0 :   dom::Selection* domSel = fs->GetSelection(SelectionType::eSpellCheck);
    2139           0 :   if (!domSel)
    2140           0 :     return;
    2141             : 
    2142           0 :   int32_t rangeCount = domSel->RangeCount();
    2143           0 :   if (rangeCount <= 0)
    2144           0 :     return;
    2145             : 
    2146           0 :   uint32_t startOffset = 0, endOffset = 0;
    2147           0 :   for (int32_t idx = 0; idx < rangeCount; idx++) {
    2148           0 :     nsRange* range = domSel->GetRangeAt(idx);
    2149           0 :     if (range->Collapsed())
    2150           0 :       continue;
    2151             : 
    2152             :     // See if the point comes after the range in which case we must continue in
    2153             :     // case there is another range after this one.
    2154           0 :     nsINode* endNode = range->GetEndContainer();
    2155           0 :     int32_t endNodeOffset = range->EndOffset();
    2156           0 :     if (nsContentUtils::ComparePoints(aNode, aNodeOffset,
    2157             :                                       endNode, endNodeOffset) >= 0)
    2158           0 :       continue;
    2159             : 
    2160             :     // At this point our point is either in this range or before it but after
    2161             :     // the previous range.  So we check to see if the range starts before the
    2162             :     // point in which case the point is in the missspelled range, otherwise it
    2163             :     // must be before the range and after the previous one if any.
    2164           0 :     nsINode* startNode = range->GetStartContainer();
    2165           0 :     int32_t startNodeOffset = range->StartOffset();
    2166           0 :     if (nsContentUtils::ComparePoints(startNode, startNodeOffset, aNode,
    2167             :                                       aNodeOffset) <= 0) {
    2168           0 :       startOffset = DOMPointToOffset(startNode, startNodeOffset);
    2169             : 
    2170           0 :       endOffset = DOMPointToOffset(endNode, endNodeOffset);
    2171             : 
    2172           0 :       if (startOffset > *aStartOffset)
    2173           0 :         *aStartOffset = startOffset;
    2174             : 
    2175           0 :       if (endOffset < *aEndOffset)
    2176           0 :         *aEndOffset = endOffset;
    2177             : 
    2178           0 :       if (aAttributes) {
    2179           0 :         nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
    2180           0 :                                NS_LITERAL_STRING("spelling"));
    2181             :       }
    2182             : 
    2183           0 :       return;
    2184             :     }
    2185             : 
    2186             :     // This range came after the point.
    2187           0 :     endOffset = DOMPointToOffset(startNode, startNodeOffset);
    2188             : 
    2189           0 :     if (idx > 0) {
    2190           0 :       nsRange* prevRange = domSel->GetRangeAt(idx - 1);
    2191           0 :       startOffset = DOMPointToOffset(prevRange->GetEndContainer(),
    2192           0 :                                      prevRange->EndOffset());
    2193             :     }
    2194             : 
    2195           0 :     if (startOffset > *aStartOffset)
    2196           0 :       *aStartOffset = startOffset;
    2197             : 
    2198           0 :     if (endOffset < *aEndOffset)
    2199           0 :       *aEndOffset = endOffset;
    2200             : 
    2201           0 :     return;
    2202             :   }
    2203             : 
    2204             :   // We never found a range that ended after the point, therefore we know that
    2205             :   // the point is not in a range, that we do not need to compute an end offset,
    2206             :   // and that we should use the end offset of the last range to compute the
    2207             :   // start offset of the text attribute range.
    2208           0 :   nsRange* prevRange = domSel->GetRangeAt(rangeCount - 1);
    2209           0 :   startOffset = DOMPointToOffset(prevRange->GetEndContainer(),
    2210           0 :                                  prevRange->EndOffset());
    2211             : 
    2212           0 :   if (startOffset > *aStartOffset)
    2213           0 :     *aStartOffset = startOffset;
    2214             : }
    2215             : 
    2216             : bool
    2217           0 : HyperTextAccessible::IsTextRole()
    2218             : {
    2219           0 :   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
    2220           0 :   if (roleMapEntry &&
    2221           0 :       (roleMapEntry->role == roles::GRAPHIC ||
    2222           0 :        roleMapEntry->role == roles::IMAGE_MAP ||
    2223           0 :        roleMapEntry->role == roles::SLIDER ||
    2224           0 :        roleMapEntry->role == roles::PROGRESSBAR ||
    2225           0 :        roleMapEntry->role == roles::SEPARATOR))
    2226           0 :     return false;
    2227             : 
    2228           0 :   return true;
    2229             : }

Generated by: LCOV version 1.13