LCOV - code coverage report
Current view: top level - editor/txtsvc - nsTextServicesDocument.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 3 1452 0.2 %
Date: 2017-07-14 16:53:18 Functions: 1 77 1.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include <stddef.h>                     // for nullptr
       7             : 
       8             : #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
       9             : #include "mozilla/dom/Selection.h"
      10             : #include "mozilla/mozalloc.h"           // for operator new, etc
      11             : #include "nsAString.h"                  // for nsAString::Length, etc
      12             : #include "nsContentUtils.h"             // for nsContentUtils
      13             : #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
      14             : #include "nsDependentSubstring.h"       // for Substring
      15             : #include "nsError.h"                    // for NS_OK, NS_ERROR_FAILURE, etc
      16             : #include "nsFilteredContentIterator.h"  // for nsFilteredContentIterator
      17             : #include "nsIContent.h"                 // for nsIContent, etc
      18             : #include "nsIContentIterator.h"         // for nsIContentIterator
      19             : #include "nsID.h"                       // for NS_GET_IID
      20             : #include "nsIDOMDocument.h"             // for nsIDOMDocument
      21             : #include "nsIDOMElement.h"              // for nsIDOMElement
      22             : #include "nsIDOMHTMLDocument.h"         // for nsIDOMHTMLDocument
      23             : #include "nsIDOMHTMLElement.h"          // for nsIDOMHTMLElement
      24             : #include "nsIDOMNode.h"                 // for nsIDOMNode, etc
      25             : #include "nsIDOMRange.h"                // for nsIDOMRange, etc
      26             : #include "nsIEditor.h"                  // for nsIEditor, etc
      27             : #include "nsINode.h"                    // for nsINode
      28             : #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor
      29             : #include "nsISelection.h"               // for nsISelection
      30             : #include "nsISelectionController.h"     // for nsISelectionController, etc
      31             : #include "nsISupportsBase.h"            // for nsISupports
      32             : #include "nsISupportsUtils.h"           // for NS_IF_ADDREF, NS_ADDREF, etc
      33             : #include "nsITextServicesFilter.h"      // for nsITextServicesFilter
      34             : #include "nsIWordBreaker.h"             // for nsWordRange, nsIWordBreaker
      35             : #include "nsRange.h"                    // for nsRange
      36             : #include "nsStaticAtom.h"               // for NS_STATIC_ATOM, etc
      37             : #include "nsString.h"                   // for nsString, nsAutoString
      38             : #include "nsTextServicesDocument.h"
      39             : #include "nscore.h"                     // for nsresult, NS_IMETHODIMP, etc
      40             : 
      41             : #define LOCK_DOC(doc)
      42             : #define UNLOCK_DOC(doc)
      43             : 
      44             : using namespace mozilla;
      45             : using namespace mozilla::dom;
      46             : 
      47             : class OffsetEntry
      48             : {
      49             : public:
      50           0 :   OffsetEntry(nsIDOMNode *aNode, int32_t aOffset, int32_t aLength)
      51           0 :     : mNode(aNode), mNodeOffset(0), mStrOffset(aOffset), mLength(aLength),
      52           0 :       mIsInsertedText(false), mIsValid(true)
      53             :   {
      54           0 :     if (mStrOffset < 1) {
      55           0 :       mStrOffset = 0;
      56             :     }
      57           0 :     if (mLength < 1) {
      58           0 :       mLength = 0;
      59             :     }
      60           0 :   }
      61             : 
      62           0 :   virtual ~OffsetEntry()
      63           0 :   {
      64           0 :     mNode       = 0;
      65           0 :     mNodeOffset = 0;
      66           0 :     mStrOffset  = 0;
      67           0 :     mLength     = 0;
      68           0 :     mIsValid    = false;
      69           0 :   }
      70             : 
      71             :   nsIDOMNode *mNode;
      72             :   int32_t mNodeOffset;
      73             :   int32_t mStrOffset;
      74             :   int32_t mLength;
      75             :   bool    mIsInsertedText;
      76             :   bool    mIsValid;
      77             : };
      78             : 
      79             : #define TS_ATOM(name_, value_) nsIAtom* nsTextServicesDocument::name_ = 0;
      80             : #include "nsTSAtomList.h" // IWYU pragma: keep
      81             : #undef TS_ATOM
      82             : 
      83           0 : nsTextServicesDocument::nsTextServicesDocument()
      84             : {
      85           0 :   mSelStartIndex  = -1;
      86           0 :   mSelStartOffset = -1;
      87           0 :   mSelEndIndex    = -1;
      88           0 :   mSelEndOffset   = -1;
      89             : 
      90           0 :   mIteratorStatus = eIsDone;
      91           0 : }
      92             : 
      93           0 : nsTextServicesDocument::~nsTextServicesDocument()
      94             : {
      95           0 :   ClearOffsetTable(&mOffsetTable);
      96           0 : }
      97             : 
      98             : #define TS_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
      99             : #include "nsTSAtomList.h" // IWYU pragma: keep
     100             : #undef TS_ATOM
     101             : 
     102             : /* static */
     103             : void
     104           3 : nsTextServicesDocument::RegisterAtoms()
     105             : {
     106             :   static const nsStaticAtom ts_atoms[] = {
     107             : #define TS_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &name_),
     108             : #include "nsTSAtomList.h" // IWYU pragma: keep
     109             : #undef TS_ATOM
     110             :   };
     111             : 
     112           3 :   NS_RegisterStaticAtoms(ts_atoms);
     113           3 : }
     114             : 
     115           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextServicesDocument)
     116           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextServicesDocument)
     117             : 
     118           0 : NS_INTERFACE_MAP_BEGIN(nsTextServicesDocument)
     119           0 :   NS_INTERFACE_MAP_ENTRY(nsITextServicesDocument)
     120           0 :   NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
     121           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITextServicesDocument)
     122           0 :   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsTextServicesDocument)
     123           0 : NS_INTERFACE_MAP_END
     124             : 
     125           0 : NS_IMPL_CYCLE_COLLECTION(nsTextServicesDocument,
     126             :                          mDOMDocument,
     127             :                          mSelCon,
     128             :                          mIterator,
     129             :                          mPrevTextBlock,
     130             :                          mNextTextBlock,
     131             :                          mExtent,
     132             :                          mTxtSvcFilter)
     133             : 
     134             : NS_IMETHODIMP
     135           0 : nsTextServicesDocument::InitWithEditor(nsIEditor *aEditor)
     136             : {
     137           0 :   nsCOMPtr<nsISelectionController> selCon;
     138           0 :   nsCOMPtr<nsIDOMDocument> doc;
     139             : 
     140           0 :   NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
     141             : 
     142             :   LOCK_DOC(this);
     143             : 
     144             :   // Check to see if we already have an mSelCon. If we do, it
     145             :   // better be the same one the editor uses!
     146             : 
     147           0 :   nsresult rv = aEditor->GetSelectionController(getter_AddRefs(selCon));
     148             : 
     149           0 :   if (NS_FAILED(rv)) {
     150             :     UNLOCK_DOC(this);
     151           0 :     return rv;
     152             :   }
     153             : 
     154           0 :   if (!selCon || (mSelCon && selCon != mSelCon)) {
     155             :     UNLOCK_DOC(this);
     156           0 :     return NS_ERROR_FAILURE;
     157             :   }
     158             : 
     159           0 :   if (!mSelCon) {
     160           0 :     mSelCon = selCon;
     161             :   }
     162             : 
     163             :   // Check to see if we already have an mDOMDocument. If we do, it
     164             :   // better be the same one the editor uses!
     165             : 
     166           0 :   rv = aEditor->GetDocument(getter_AddRefs(doc));
     167             : 
     168           0 :   if (NS_FAILED(rv)) {
     169             :     UNLOCK_DOC(this);
     170           0 :     return rv;
     171             :   }
     172             : 
     173           0 :   if (!doc || (mDOMDocument && doc != mDOMDocument)) {
     174             :     UNLOCK_DOC(this);
     175           0 :     return NS_ERROR_FAILURE;
     176             :   }
     177             : 
     178           0 :   if (!mDOMDocument) {
     179           0 :     mDOMDocument = doc;
     180             : 
     181           0 :     rv = CreateDocumentContentIterator(getter_AddRefs(mIterator));
     182             : 
     183           0 :     if (NS_FAILED(rv)) {
     184             :       UNLOCK_DOC(this);
     185           0 :       return rv;
     186             :     }
     187             : 
     188           0 :     mIteratorStatus = nsTextServicesDocument::eIsDone;
     189             : 
     190           0 :     rv = FirstBlock();
     191             : 
     192           0 :     if (NS_FAILED(rv)) {
     193             :       UNLOCK_DOC(this);
     194           0 :       return rv;
     195             :     }
     196             :   }
     197             : 
     198           0 :   mEditor = do_GetWeakReference(aEditor);
     199             : 
     200           0 :   rv = aEditor->AddEditActionListener(this);
     201             : 
     202             :   UNLOCK_DOC(this);
     203             : 
     204           0 :   return rv;
     205             : }
     206             : 
     207             : NS_IMETHODIMP
     208           0 : nsTextServicesDocument::GetDocument(nsIDOMDocument **aDoc)
     209             : {
     210           0 :   NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
     211             : 
     212           0 :   *aDoc = nullptr; // init out param
     213           0 :   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_NOT_INITIALIZED);
     214             : 
     215           0 :   *aDoc = mDOMDocument;
     216           0 :   NS_ADDREF(*aDoc);
     217             : 
     218           0 :   return NS_OK;
     219             : }
     220             : 
     221             : NS_IMETHODIMP
     222           0 : nsTextServicesDocument::SetExtent(nsIDOMRange* aDOMRange)
     223             : {
     224           0 :   NS_ENSURE_ARG_POINTER(aDOMRange);
     225           0 :   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
     226             : 
     227             :   LOCK_DOC(this);
     228             : 
     229             :   // We need to store a copy of aDOMRange since we don't
     230             :   // know where it came from.
     231             : 
     232           0 :   mExtent = static_cast<nsRange*>(aDOMRange)->CloneRange();
     233             : 
     234             :   // Create a new iterator based on our new extent range.
     235             : 
     236           0 :   nsresult rv = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
     237             : 
     238           0 :   if (NS_FAILED(rv)) {
     239             :     UNLOCK_DOC(this);
     240           0 :     return rv;
     241             :   }
     242             : 
     243             :   // Now position the iterator at the start of the first block
     244             :   // in the range.
     245             : 
     246           0 :   mIteratorStatus = nsTextServicesDocument::eIsDone;
     247             : 
     248           0 :   rv = FirstBlock();
     249             : 
     250             :   UNLOCK_DOC(this);
     251             : 
     252           0 :   return rv;
     253             : }
     254             : 
     255             : NS_IMETHODIMP
     256           0 : nsTextServicesDocument::ExpandRangeToWordBoundaries(nsIDOMRange *aRange)
     257             : {
     258           0 :   NS_ENSURE_ARG_POINTER(aRange);
     259           0 :   RefPtr<nsRange> range = static_cast<nsRange*>(aRange);
     260             : 
     261             :   // Get the end points of the range.
     262             : 
     263           0 :   nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
     264             :   int32_t rngStartOffset, rngEndOffset;
     265             : 
     266           0 :   nsresult rv = GetRangeEndPoints(range, getter_AddRefs(rngStartNode),
     267             :                                   &rngStartOffset,
     268           0 :                                   getter_AddRefs(rngEndNode),
     269           0 :                                   &rngEndOffset);
     270             : 
     271           0 :   NS_ENSURE_SUCCESS(rv, rv);
     272             : 
     273             :   // Create a content iterator based on the range.
     274             : 
     275           0 :   nsCOMPtr<nsIContentIterator> iter;
     276           0 :   rv = CreateContentIterator(range, getter_AddRefs(iter));
     277             : 
     278           0 :   NS_ENSURE_SUCCESS(rv, rv);
     279             : 
     280             :   // Find the first text node in the range.
     281             : 
     282             :   TSDIteratorStatus iterStatus;
     283             : 
     284           0 :   rv = FirstTextNode(iter, &iterStatus);
     285           0 :   NS_ENSURE_SUCCESS(rv, rv);
     286             : 
     287           0 :   if (iterStatus == nsTextServicesDocument::eIsDone) {
     288             :     // No text was found so there's no adjustment necessary!
     289           0 :     return NS_OK;
     290             :   }
     291             : 
     292           0 :   nsINode *firstText = iter->GetCurrentNode();
     293           0 :   NS_ENSURE_TRUE(firstText, NS_ERROR_FAILURE);
     294             : 
     295             :   // Find the last text node in the range.
     296             : 
     297           0 :   rv = LastTextNode(iter, &iterStatus);
     298           0 :   NS_ENSURE_SUCCESS(rv, rv);
     299             : 
     300           0 :   if (iterStatus == nsTextServicesDocument::eIsDone) {
     301             :     // We should never get here because a first text block
     302             :     // was found above.
     303           0 :     NS_ASSERTION(false, "Found a first without a last!");
     304           0 :     return NS_ERROR_FAILURE;
     305             :   }
     306             : 
     307           0 :   nsINode *lastText = iter->GetCurrentNode();
     308           0 :   NS_ENSURE_TRUE(lastText, NS_ERROR_FAILURE);
     309             : 
     310             :   // Now make sure our end points are in terms of text nodes in the range!
     311             : 
     312           0 :   nsCOMPtr<nsIDOMNode> firstTextNode = do_QueryInterface(firstText);
     313           0 :   NS_ENSURE_TRUE(firstTextNode, NS_ERROR_FAILURE);
     314             : 
     315           0 :   if (rngStartNode != firstTextNode) {
     316             :     // The range includes the start of the first text node!
     317           0 :     rngStartNode = firstTextNode;
     318           0 :     rngStartOffset = 0;
     319             :   }
     320             : 
     321           0 :   nsCOMPtr<nsIDOMNode> lastTextNode = do_QueryInterface(lastText);
     322           0 :   NS_ENSURE_TRUE(lastTextNode, NS_ERROR_FAILURE);
     323             : 
     324           0 :   if (rngEndNode != lastTextNode) {
     325             :     // The range includes the end of the last text node!
     326           0 :     rngEndNode = lastTextNode;
     327           0 :     nsAutoString str;
     328           0 :     lastTextNode->GetNodeValue(str);
     329           0 :     rngEndOffset = str.Length();
     330             :   }
     331             : 
     332             :   // Create a doc iterator so that we can scan beyond
     333             :   // the bounds of the extent range.
     334             : 
     335           0 :   nsCOMPtr<nsIContentIterator> docIter;
     336           0 :   rv = CreateDocumentContentIterator(getter_AddRefs(docIter));
     337           0 :   NS_ENSURE_SUCCESS(rv, rv);
     338             : 
     339             :   // Grab all the text in the block containing our
     340             :   // first text node.
     341             : 
     342           0 :   rv = docIter->PositionAt(firstText);
     343           0 :   NS_ENSURE_SUCCESS(rv, rv);
     344             : 
     345           0 :   iterStatus = nsTextServicesDocument::eValid;
     346             : 
     347           0 :   nsTArray<OffsetEntry*> offsetTable;
     348           0 :   nsAutoString blockStr;
     349             : 
     350           0 :   rv = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
     351           0 :                          nullptr, &blockStr);
     352           0 :   if (NS_FAILED(rv)) {
     353           0 :     ClearOffsetTable(&offsetTable);
     354           0 :     return rv;
     355             :   }
     356             : 
     357           0 :   nsCOMPtr<nsIDOMNode> wordStartNode, wordEndNode;
     358             :   int32_t wordStartOffset, wordEndOffset;
     359             : 
     360           0 :   rv = FindWordBounds(&offsetTable, &blockStr,
     361             :                       rngStartNode, rngStartOffset,
     362           0 :                       getter_AddRefs(wordStartNode), &wordStartOffset,
     363           0 :                       getter_AddRefs(wordEndNode), &wordEndOffset);
     364             : 
     365           0 :   ClearOffsetTable(&offsetTable);
     366             : 
     367           0 :   NS_ENSURE_SUCCESS(rv, rv);
     368             : 
     369           0 :   rngStartNode = wordStartNode;
     370           0 :   rngStartOffset = wordStartOffset;
     371             : 
     372             :   // Grab all the text in the block containing our
     373             :   // last text node.
     374             : 
     375           0 :   rv = docIter->PositionAt(lastText);
     376           0 :   NS_ENSURE_SUCCESS(rv, rv);
     377             : 
     378           0 :   iterStatus = nsTextServicesDocument::eValid;
     379             : 
     380           0 :   rv = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
     381           0 :                          nullptr, &blockStr);
     382           0 :   if (NS_FAILED(rv)) {
     383           0 :     ClearOffsetTable(&offsetTable);
     384           0 :     return rv;
     385             :   }
     386             : 
     387           0 :   rv = FindWordBounds(&offsetTable, &blockStr,
     388             :                       rngEndNode, rngEndOffset,
     389           0 :                       getter_AddRefs(wordStartNode), &wordStartOffset,
     390           0 :                       getter_AddRefs(wordEndNode), &wordEndOffset);
     391             : 
     392           0 :   ClearOffsetTable(&offsetTable);
     393             : 
     394           0 :   NS_ENSURE_SUCCESS(rv, rv);
     395             : 
     396             :   // To prevent expanding the range too much, we only change
     397             :   // rngEndNode and rngEndOffset if it isn't already at the start of the
     398             :   // word and isn't equivalent to rngStartNode and rngStartOffset.
     399             : 
     400           0 :   if (rngEndNode != wordStartNode ||
     401           0 :       rngEndOffset != wordStartOffset ||
     402           0 :       (rngEndNode == rngStartNode && rngEndOffset == rngStartOffset)) {
     403           0 :     rngEndNode = wordEndNode;
     404           0 :     rngEndOffset = wordEndOffset;
     405             :   }
     406             : 
     407             :   // Now adjust the range so that it uses our new
     408             :   // end points.
     409           0 :   nsCOMPtr<nsINode> startNode = do_QueryInterface(rngStartNode);
     410           0 :   nsCOMPtr<nsINode> endNode = do_QueryInterface(rngEndNode);
     411           0 :   rv = range->SetStartAndEnd(startNode, rngStartOffset, endNode, rngEndOffset);
     412           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     413           0 :     return rv;
     414             :   }
     415           0 :   return NS_OK;
     416             : }
     417             : 
     418             : NS_IMETHODIMP
     419           0 : nsTextServicesDocument::SetFilter(nsITextServicesFilter *aFilter)
     420             : {
     421             :   // Hang on to the filter so we can set it into the filtered iterator.
     422           0 :   mTxtSvcFilter = aFilter;
     423             : 
     424           0 :   return NS_OK;
     425             : }
     426             : 
     427             : NS_IMETHODIMP
     428           0 : nsTextServicesDocument::GetCurrentTextBlock(nsString *aStr)
     429             : {
     430           0 :   NS_ENSURE_TRUE(aStr, NS_ERROR_NULL_POINTER);
     431             : 
     432           0 :   aStr->Truncate();
     433             : 
     434           0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
     435             : 
     436             :   LOCK_DOC(this);
     437             : 
     438           0 :   nsresult rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     439           0 :                                   mExtent, aStr);
     440             : 
     441             :   UNLOCK_DOC(this);
     442             : 
     443           0 :   return rv;
     444             : }
     445             : 
     446             : NS_IMETHODIMP
     447           0 : nsTextServicesDocument::FirstBlock()
     448             : {
     449           0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
     450             : 
     451             :   LOCK_DOC(this);
     452             : 
     453           0 :   nsresult rv = FirstTextNode(mIterator, &mIteratorStatus);
     454             : 
     455           0 :   if (NS_FAILED(rv)) {
     456             :     UNLOCK_DOC(this);
     457           0 :     return rv;
     458             :   }
     459             : 
     460             :   // Keep track of prev and next blocks, just in case
     461             :   // the text service blows away the current block.
     462             : 
     463           0 :   if (mIteratorStatus == nsTextServicesDocument::eValid) {
     464           0 :     mPrevTextBlock  = nullptr;
     465           0 :     rv = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
     466             :   } else {
     467             :     // There's no text block in the document!
     468             : 
     469           0 :     mPrevTextBlock  = nullptr;
     470           0 :     mNextTextBlock  = nullptr;
     471             :   }
     472             : 
     473             :   UNLOCK_DOC(this);
     474             : 
     475             :   // XXX Result of FirstTextNode() or GetFirstTextNodeInNextBlock().
     476           0 :   return rv;
     477             : }
     478             : 
     479             : NS_IMETHODIMP
     480           0 : nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus,
     481             :                                           int32_t *aSelOffset,
     482             :                                           int32_t *aSelLength)
     483             : {
     484           0 :   NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
     485             : 
     486             :   LOCK_DOC(this);
     487             : 
     488           0 :   mIteratorStatus = nsTextServicesDocument::eIsDone;
     489             : 
     490           0 :   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
     491           0 :   *aSelOffset = *aSelLength = -1;
     492             : 
     493           0 :   if (!mSelCon || !mIterator) {
     494             :     UNLOCK_DOC(this);
     495           0 :     return NS_ERROR_FAILURE;
     496             :   }
     497             : 
     498           0 :   nsCOMPtr<nsISelection> domSelection;
     499           0 :   nsresult rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
     500           0 :                                       getter_AddRefs(domSelection));
     501           0 :   if (NS_FAILED(rv)) {
     502             :     UNLOCK_DOC(this);
     503           0 :     return rv;
     504             :   }
     505             : 
     506           0 :   RefPtr<Selection> selection = domSelection->AsSelection();
     507             : 
     508           0 :   bool isCollapsed = selection->IsCollapsed();
     509             : 
     510           0 :   nsCOMPtr<nsIContentIterator> iter;
     511           0 :   RefPtr<nsRange> range;
     512           0 :   nsCOMPtr<nsIDOMNode>         parent;
     513             :   int32_t rangeCount, offset;
     514             : 
     515           0 :   if (isCollapsed) {
     516             :     // We have a caret. Check if the caret is in a text node.
     517             :     // If it is, make the text node's block the current block.
     518             :     // If the caret isn't in a text node, search forwards in
     519             :     // the document, till we find a text node.
     520             : 
     521           0 :     range = selection->GetRangeAt(0);
     522             : 
     523           0 :     if (!range) {
     524             :       UNLOCK_DOC(this);
     525           0 :       return NS_ERROR_FAILURE;
     526             :     }
     527             : 
     528           0 :     rv = range->GetStartContainer(getter_AddRefs(parent));
     529             : 
     530           0 :     if (NS_FAILED(rv)) {
     531             :       UNLOCK_DOC(this);
     532           0 :       return rv;
     533             :     }
     534             : 
     535           0 :     if (!parent) {
     536             :       UNLOCK_DOC(this);
     537           0 :       return NS_ERROR_FAILURE;
     538             :     }
     539             : 
     540           0 :     rv = range->GetStartOffset(&offset);
     541             : 
     542           0 :     if (NS_FAILED(rv)) {
     543             :       UNLOCK_DOC(this);
     544           0 :       return rv;
     545             :     }
     546             : 
     547           0 :     if (IsTextNode(parent)) {
     548             :       // The caret is in a text node. Find the beginning
     549             :       // of the text block containing this text node and
     550             :       // return.
     551             : 
     552           0 :       nsCOMPtr<nsIContent> content(do_QueryInterface(parent));
     553             : 
     554           0 :       if (!content) {
     555             :         UNLOCK_DOC(this);
     556           0 :         return NS_ERROR_FAILURE;
     557             :       }
     558             : 
     559           0 :       rv = mIterator->PositionAt(content);
     560             : 
     561           0 :       if (NS_FAILED(rv)) {
     562             :         UNLOCK_DOC(this);
     563           0 :         return rv;
     564             :       }
     565             : 
     566           0 :       rv = FirstTextNodeInCurrentBlock(mIterator);
     567             : 
     568           0 :       if (NS_FAILED(rv)) {
     569             :         UNLOCK_DOC(this);
     570           0 :         return rv;
     571             :       }
     572             : 
     573           0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     574             : 
     575           0 :       rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     576           0 :                              mExtent, nullptr);
     577             : 
     578           0 :       if (NS_FAILED(rv)) {
     579             :         UNLOCK_DOC(this);
     580           0 :         return rv;
     581             :       }
     582             : 
     583           0 :       rv = GetSelection(aSelStatus, aSelOffset, aSelLength);
     584             : 
     585           0 :       if (NS_FAILED(rv)) {
     586             :         UNLOCK_DOC(this);
     587           0 :         return rv;
     588             :       }
     589             : 
     590           0 :       if (*aSelStatus == nsITextServicesDocument::eBlockContains) {
     591           0 :         rv = SetSelectionInternal(*aSelOffset, *aSelLength, false);
     592             :       }
     593             :     } else {
     594             :       // The caret isn't in a text node. Create an iterator
     595             :       // based on a range that extends from the current caret
     596             :       // position to the end of the document, then walk forwards
     597             :       // till you find a text node, then find the beginning of it's block.
     598             : 
     599           0 :       rv = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false,
     600           0 :                                                       getter_AddRefs(range));
     601             : 
     602           0 :       if (NS_FAILED(rv)) {
     603             :         UNLOCK_DOC(this);
     604           0 :         return rv;
     605             :       }
     606             : 
     607           0 :       rv = range->GetCollapsed(&isCollapsed);
     608             : 
     609           0 :       if (NS_FAILED(rv)) {
     610             :         UNLOCK_DOC(this);
     611           0 :         return rv;
     612             :       }
     613             : 
     614           0 :       if (isCollapsed) {
     615             :         // If we get here, the range is collapsed because there is nothing after
     616             :         // the caret! Just return NS_OK;
     617             : 
     618             :         UNLOCK_DOC(this);
     619           0 :         return NS_OK;
     620             :       }
     621             : 
     622           0 :       rv = CreateContentIterator(range, getter_AddRefs(iter));
     623             : 
     624           0 :       if (NS_FAILED(rv)) {
     625             :         UNLOCK_DOC(this);
     626           0 :         return rv;
     627             :       }
     628             : 
     629           0 :       iter->First();
     630             : 
     631           0 :       nsCOMPtr<nsIContent> content;
     632           0 :       while (!iter->IsDone()) {
     633           0 :         content = do_QueryInterface(iter->GetCurrentNode());
     634             : 
     635           0 :         if (IsTextNode(content)) {
     636           0 :           break;
     637             :         }
     638             : 
     639           0 :         content = nullptr;
     640             : 
     641           0 :         iter->Next();
     642             :       }
     643             : 
     644           0 :       if (!content) {
     645             :         UNLOCK_DOC(this);
     646           0 :         return NS_OK;
     647             :       }
     648             : 
     649           0 :       rv = mIterator->PositionAt(content);
     650             : 
     651           0 :       if (NS_FAILED(rv)) {
     652             :         UNLOCK_DOC(this);
     653           0 :         return rv;
     654             :       }
     655             : 
     656           0 :       rv = FirstTextNodeInCurrentBlock(mIterator);
     657             : 
     658           0 :       if (NS_FAILED(rv)) {
     659             :         UNLOCK_DOC(this);
     660           0 :         return rv;
     661             :       }
     662             : 
     663           0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     664             : 
     665           0 :       rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     666           0 :                              mExtent, nullptr);
     667             : 
     668           0 :       if (NS_FAILED(rv)) {
     669             :         UNLOCK_DOC(this);
     670           0 :         return rv;
     671             :       }
     672             : 
     673           0 :       rv = GetSelection(aSelStatus, aSelOffset, aSelLength);
     674             : 
     675           0 :       if (NS_FAILED(rv)) {
     676             :         UNLOCK_DOC(this);
     677           0 :         return rv;
     678             :       }
     679             :     }
     680             : 
     681             :     UNLOCK_DOC(this);
     682             : 
     683             :     // Result of SetSelectionInternal() in the |if| block or NS_OK.
     684           0 :     return rv;
     685             :   }
     686             : 
     687             :   // If we get here, we have an uncollapsed selection!
     688             :   // Look backwards through each range in the selection till you
     689             :   // find the first text node. If you find one, find the
     690             :   // beginning of its text block, and make it the current
     691             :   // block.
     692             : 
     693           0 :   rv = selection->GetRangeCount(&rangeCount);
     694             : 
     695           0 :   if (NS_FAILED(rv)) {
     696             :     UNLOCK_DOC(this);
     697           0 :     return rv;
     698             :   }
     699             : 
     700           0 :   NS_ASSERTION(rangeCount > 0, "Unexpected range count!");
     701             : 
     702           0 :   if (rangeCount <= 0) {
     703             :     UNLOCK_DOC(this);
     704           0 :     return NS_OK;
     705             :   }
     706             : 
     707             :   // XXX: We may need to add some code here to make sure
     708             :   //      the ranges are sorted in document appearance order!
     709             : 
     710           0 :   for (int32_t i = rangeCount - 1; i >= 0; i--) {
     711             :     // Get the i'th range from the selection.
     712             : 
     713           0 :     range = selection->GetRangeAt(i);
     714             : 
     715           0 :     if (!range) {
     716             :       UNLOCK_DOC(this);
     717           0 :       return NS_OK; // XXX Really?
     718             :     }
     719             : 
     720             :     // Create an iterator for the range.
     721             : 
     722           0 :     rv = CreateContentIterator(range, getter_AddRefs(iter));
     723             : 
     724           0 :     if (NS_FAILED(rv)) {
     725             :       UNLOCK_DOC(this);
     726           0 :       return rv;
     727             :     }
     728             : 
     729           0 :     iter->Last();
     730             : 
     731             :     // Now walk through the range till we find a text node.
     732             : 
     733           0 :     while (!iter->IsDone()) {
     734           0 :       if (iter->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
     735             :         // We found a text node, so position the document's
     736             :         // iterator at the beginning of the block, then get
     737             :         // the selection in terms of the string offset.
     738           0 :         nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
     739             : 
     740           0 :         rv = mIterator->PositionAt(content);
     741             : 
     742           0 :         if (NS_FAILED(rv)) {
     743             :           UNLOCK_DOC(this);
     744           0 :           return rv;
     745             :         }
     746             : 
     747           0 :         rv = FirstTextNodeInCurrentBlock(mIterator);
     748             : 
     749           0 :         if (NS_FAILED(rv)) {
     750             :           UNLOCK_DOC(this);
     751           0 :           return rv;
     752             :         }
     753             : 
     754           0 :         mIteratorStatus = nsTextServicesDocument::eValid;
     755             : 
     756           0 :         rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     757           0 :                                mExtent, nullptr);
     758             : 
     759           0 :         if (NS_FAILED(rv)) {
     760             :           UNLOCK_DOC(this);
     761           0 :           return rv;
     762             :         }
     763             : 
     764           0 :         rv = GetSelection(aSelStatus, aSelOffset, aSelLength);
     765             : 
     766             :         UNLOCK_DOC(this);
     767             : 
     768           0 :         return rv;
     769             : 
     770             :       }
     771             : 
     772           0 :       iter->Prev();
     773             :     }
     774             :   }
     775             : 
     776             :   // If we get here, we didn't find any text node in the selection!
     777             :   // Create a range that extends from the end of the selection,
     778             :   // to the end of the document, then iterate forwards through
     779             :   // it till you find a text node!
     780             : 
     781           0 :   range = selection->GetRangeAt(rangeCount - 1);
     782             : 
     783           0 :   if (!range) {
     784             :     UNLOCK_DOC(this);
     785           0 :     return NS_ERROR_FAILURE;
     786             :   }
     787             : 
     788           0 :   rv = range->GetEndContainer(getter_AddRefs(parent));
     789             : 
     790           0 :   if (NS_FAILED(rv)) {
     791             :     UNLOCK_DOC(this);
     792           0 :     return rv;
     793             :   }
     794             : 
     795           0 :   if (!parent) {
     796             :     UNLOCK_DOC(this);
     797           0 :     return NS_ERROR_FAILURE;
     798             :   }
     799             : 
     800           0 :   rv = range->GetEndOffset(&offset);
     801             : 
     802           0 :   if (NS_FAILED(rv)) {
     803             :     UNLOCK_DOC(this);
     804           0 :     return rv;
     805             :   }
     806             : 
     807           0 :   rv = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false,
     808           0 :                                                   getter_AddRefs(range));
     809             : 
     810           0 :   if (NS_FAILED(rv)) {
     811             :     UNLOCK_DOC(this);
     812           0 :     return rv;
     813             :   }
     814             : 
     815           0 :   rv = range->GetCollapsed(&isCollapsed);
     816             : 
     817           0 :   if (NS_FAILED(rv)) {
     818             :     UNLOCK_DOC(this);
     819           0 :     return rv;
     820             :   }
     821             : 
     822           0 :   if (isCollapsed) {
     823             :     // If we get here, the range is collapsed because there is nothing after
     824             :     // the current selection! Just return NS_OK;
     825             : 
     826             :     UNLOCK_DOC(this);
     827           0 :     return NS_OK;
     828             :   }
     829             : 
     830           0 :   rv = CreateContentIterator(range, getter_AddRefs(iter));
     831             : 
     832           0 :   if (NS_FAILED(rv)) {
     833             :     UNLOCK_DOC(this);
     834           0 :     return rv;
     835             :   }
     836             : 
     837           0 :   iter->First();
     838             : 
     839           0 :   while (!iter->IsDone()) {
     840           0 :     if (iter->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
     841             :       // We found a text node! Adjust the document's iterator to point
     842             :       // to the beginning of its text block, then get the current selection.
     843           0 :       nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
     844             : 
     845           0 :       rv = mIterator->PositionAt(content);
     846             : 
     847           0 :       if (NS_FAILED(rv)) {
     848             :         UNLOCK_DOC(this);
     849           0 :         return rv;
     850             :       }
     851             : 
     852           0 :       rv = FirstTextNodeInCurrentBlock(mIterator);
     853             : 
     854           0 :       if (NS_FAILED(rv)) {
     855             :         UNLOCK_DOC(this);
     856           0 :         return rv;
     857             :       }
     858             : 
     859             : 
     860           0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     861             : 
     862           0 :       rv = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
     863           0 :                              mExtent, nullptr);
     864             : 
     865           0 :       if (NS_FAILED(rv)) {
     866             :         UNLOCK_DOC(this);
     867           0 :         return rv;
     868             :       }
     869             : 
     870           0 :       rv = GetSelection(aSelStatus, aSelOffset, aSelLength);
     871             : 
     872             :       UNLOCK_DOC(this);
     873             : 
     874           0 :       return rv;
     875             :     }
     876             : 
     877           0 :     iter->Next();
     878             :   }
     879             : 
     880             :   // If we get here, we didn't find any block before or inside
     881             :   // the selection! Just return OK.
     882             : 
     883             :   UNLOCK_DOC(this);
     884             : 
     885           0 :   return NS_OK;
     886             : }
     887             : 
     888             : NS_IMETHODIMP
     889           0 : nsTextServicesDocument::PrevBlock()
     890             : {
     891           0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
     892             : 
     893             :   LOCK_DOC(this);
     894             : 
     895           0 :   if (mIteratorStatus == nsTextServicesDocument::eIsDone) {
     896           0 :     return NS_OK;
     897             :   }
     898             : 
     899           0 :   switch (mIteratorStatus) {
     900             :     case nsTextServicesDocument::eValid:
     901             :     case nsTextServicesDocument::eNext: {
     902             : 
     903           0 :       nsresult rv = FirstTextNodeInPrevBlock(mIterator);
     904             : 
     905           0 :       if (NS_FAILED(rv)) {
     906           0 :         mIteratorStatus = nsTextServicesDocument::eIsDone;
     907             :         UNLOCK_DOC(this);
     908           0 :         return rv;
     909             :       }
     910             : 
     911           0 :       if (mIterator->IsDone()) {
     912           0 :         mIteratorStatus = nsTextServicesDocument::eIsDone;
     913             :         UNLOCK_DOC(this);
     914           0 :         return NS_OK;
     915             :       }
     916             : 
     917           0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     918           0 :       break;
     919             :     }
     920             :     case nsTextServicesDocument::ePrev:
     921             : 
     922             :       // The iterator already points to the previous
     923             :       // block, so don't do anything.
     924             : 
     925           0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     926           0 :       break;
     927             : 
     928             :     default:
     929             : 
     930           0 :       mIteratorStatus = nsTextServicesDocument::eIsDone;
     931           0 :       break;
     932             :   }
     933             : 
     934             :   // Keep track of prev and next blocks, just in case
     935             :   // the text service blows away the current block.
     936           0 :   nsresult rv = NS_OK;
     937           0 :   if (mIteratorStatus == nsTextServicesDocument::eValid) {
     938           0 :     GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
     939           0 :     rv = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
     940             :   } else {
     941             :     // We must be done!
     942           0 :     mPrevTextBlock = nullptr;
     943           0 :     mNextTextBlock = nullptr;
     944             :   }
     945             : 
     946             :   UNLOCK_DOC(this);
     947             : 
     948             :   // XXX The result of GetFirstTextNodeInNextBlock() or NS_OK.
     949           0 :   return rv;
     950             : }
     951             : 
     952             : NS_IMETHODIMP
     953           0 : nsTextServicesDocument::NextBlock()
     954             : {
     955           0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
     956             : 
     957             :   LOCK_DOC(this);
     958             : 
     959           0 :   if (mIteratorStatus == nsTextServicesDocument::eIsDone) {
     960           0 :     return NS_OK;
     961             :   }
     962             : 
     963           0 :   switch (mIteratorStatus) {
     964             :     case nsTextServicesDocument::eValid: {
     965             : 
     966             :       // Advance the iterator to the next text block.
     967             : 
     968           0 :       nsresult rv = FirstTextNodeInNextBlock(mIterator);
     969             : 
     970           0 :       if (NS_FAILED(rv)) {
     971           0 :         mIteratorStatus = nsTextServicesDocument::eIsDone;
     972             :         UNLOCK_DOC(this);
     973           0 :         return rv;
     974             :       }
     975             : 
     976           0 :       if (mIterator->IsDone()) {
     977           0 :         mIteratorStatus = nsTextServicesDocument::eIsDone;
     978             :         UNLOCK_DOC(this);
     979           0 :         return NS_OK;
     980             :       }
     981             : 
     982           0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     983           0 :       break;
     984             :     }
     985             :     case nsTextServicesDocument::eNext:
     986             : 
     987             :       // The iterator already points to the next block,
     988             :       // so don't do anything to it!
     989             : 
     990           0 :       mIteratorStatus = nsTextServicesDocument::eValid;
     991           0 :       break;
     992             : 
     993             :     case nsTextServicesDocument::ePrev:
     994             : 
     995             :       // If the iterator is pointing to the previous block,
     996             :       // we know that there is no next text block! Just
     997             :       // fall through to the default case!
     998             : 
     999             :     default:
    1000             : 
    1001           0 :       mIteratorStatus = nsTextServicesDocument::eIsDone;
    1002           0 :       break;
    1003             :   }
    1004             : 
    1005             :   // Keep track of prev and next blocks, just in case
    1006             :   // the text service blows away the current block.
    1007           0 :   nsresult rv = NS_OK;
    1008           0 :   if (mIteratorStatus == nsTextServicesDocument::eValid) {
    1009           0 :     GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
    1010           0 :     rv = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
    1011             :   } else {
    1012             :     // We must be done.
    1013           0 :     mPrevTextBlock = nullptr;
    1014           0 :     mNextTextBlock = nullptr;
    1015             :   }
    1016             : 
    1017             :   UNLOCK_DOC(this);
    1018             : 
    1019             :   // The result of GetFirstTextNodeInNextBlock() or NS_OK.
    1020           0 :   return rv;
    1021             : }
    1022             : 
    1023             : NS_IMETHODIMP
    1024           0 : nsTextServicesDocument::IsDone(bool *aIsDone)
    1025             : {
    1026           0 :   NS_ENSURE_TRUE(aIsDone, NS_ERROR_NULL_POINTER);
    1027             : 
    1028           0 :   *aIsDone = false;
    1029             : 
    1030           0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
    1031             : 
    1032             :   LOCK_DOC(this);
    1033             : 
    1034           0 :   *aIsDone = (mIteratorStatus == nsTextServicesDocument::eIsDone) ? true : false;
    1035             : 
    1036             :   UNLOCK_DOC(this);
    1037             : 
    1038           0 :   return NS_OK;
    1039             : }
    1040             : 
    1041             : NS_IMETHODIMP
    1042           0 : nsTextServicesDocument::SetSelection(int32_t aOffset, int32_t aLength)
    1043             : {
    1044           0 :   NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
    1045             : 
    1046             :   LOCK_DOC(this);
    1047             : 
    1048           0 :   nsresult rv = SetSelectionInternal(aOffset, aLength, true);
    1049             : 
    1050             :   UNLOCK_DOC(this);
    1051             : 
    1052             :   //**** KDEBUG ****
    1053             :   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1054             :   //**** KDEBUG ****
    1055             : 
    1056           0 :   return rv;
    1057             : }
    1058             : 
    1059             : NS_IMETHODIMP
    1060           0 : nsTextServicesDocument::ScrollSelectionIntoView()
    1061             : {
    1062           0 :   NS_ENSURE_TRUE(mSelCon, NS_ERROR_FAILURE);
    1063             : 
    1064             :   LOCK_DOC(this);
    1065             : 
    1066             :   // After ScrollSelectionIntoView(), the pending notifications might be flushed
    1067             :   // and PresShell/PresContext/Frames may be dead. See bug 418470.
    1068             :   nsresult rv =
    1069           0 :     mSelCon->ScrollSelectionIntoView(
    1070             :       nsISelectionController::SELECTION_NORMAL,
    1071             :       nsISelectionController::SELECTION_FOCUS_REGION,
    1072           0 :       nsISelectionController::SCROLL_SYNCHRONOUS);
    1073             : 
    1074             :   UNLOCK_DOC(this);
    1075             : 
    1076           0 :   return rv;
    1077             : }
    1078             : 
    1079             : NS_IMETHODIMP
    1080           0 : nsTextServicesDocument::DeleteSelection()
    1081             : {
    1082             :   // We don't allow deletion during a collapsed selection!
    1083           0 :   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
    1084           0 :   NS_ASSERTION(editor, "DeleteSelection called without an editor present!");
    1085           0 :   NS_ASSERTION(SelectionIsValid(), "DeleteSelection called without a valid selection!");
    1086             : 
    1087           0 :   if (!editor || !SelectionIsValid()) {
    1088           0 :     return NS_ERROR_FAILURE;
    1089             :   }
    1090           0 :   if (SelectionIsCollapsed()) {
    1091           0 :     return NS_OK;
    1092             :   }
    1093             : 
    1094             :   LOCK_DOC(this);
    1095             : 
    1096             :   //**** KDEBUG ****
    1097             :   // printf("\n---- Before Delete\n");
    1098             :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1099             :   // PrintOffsetTable();
    1100             :   //**** KDEBUG ****
    1101             : 
    1102             :   // If we have an mExtent, save off its current set of
    1103             :   // end points so we can compare them against mExtent's
    1104             :   // set after the deletion of the content.
    1105             : 
    1106           0 :   nsCOMPtr<nsIDOMNode> origStartNode, origEndNode;
    1107           0 :   int32_t origStartOffset = 0, origEndOffset = 0;
    1108             : 
    1109           0 :   if (mExtent) {
    1110             :     nsresult rv =
    1111           0 :       GetRangeEndPoints(mExtent,
    1112           0 :                         getter_AddRefs(origStartNode), &origStartOffset,
    1113           0 :                         getter_AddRefs(origEndNode), &origEndOffset);
    1114             : 
    1115           0 :     if (NS_FAILED(rv)) {
    1116             :       UNLOCK_DOC(this);
    1117           0 :       return rv;
    1118             :     }
    1119             :   }
    1120             : 
    1121             :   int32_t selLength;
    1122             :   OffsetEntry *entry, *newEntry;
    1123             : 
    1124           0 :   for (int32_t i = mSelStartIndex; i <= mSelEndIndex; i++) {
    1125           0 :     entry = mOffsetTable[i];
    1126             : 
    1127           0 :     if (i == mSelStartIndex) {
    1128             :       // Calculate the length of the selection. Note that the
    1129             :       // selection length can be zero if the start of the selection
    1130             :       // is at the very end of a text node entry.
    1131             : 
    1132           0 :       if (entry->mIsInsertedText) {
    1133             :         // Inserted text offset entries have no width when
    1134             :         // talking in terms of string offsets! If the beginning
    1135             :         // of the selection is in an inserted text offset entry,
    1136             :         // the caret is always at the end of the entry!
    1137             : 
    1138           0 :         selLength = 0;
    1139             :       } else {
    1140           0 :         selLength = entry->mLength - (mSelStartOffset - entry->mStrOffset);
    1141             :       }
    1142             : 
    1143           0 :       if (selLength > 0 && mSelStartOffset > entry->mStrOffset) {
    1144             :         // Selection doesn't start at the beginning of the
    1145             :         // text node entry. We need to split this entry into
    1146             :         // two pieces, the piece before the selection, and
    1147             :         // the piece inside the selection.
    1148             : 
    1149           0 :         nsresult rv = SplitOffsetEntry(i, selLength);
    1150             : 
    1151           0 :         if (NS_FAILED(rv)) {
    1152             :           UNLOCK_DOC(this);
    1153           0 :           return rv;
    1154             :         }
    1155             : 
    1156             :         // Adjust selection indexes to account for new entry:
    1157             : 
    1158           0 :         ++mSelStartIndex;
    1159           0 :         ++mSelEndIndex;
    1160           0 :         ++i;
    1161             : 
    1162           0 :         entry = mOffsetTable[i];
    1163             :       }
    1164             : 
    1165             : 
    1166           0 :       if (selLength > 0 && mSelStartIndex < mSelEndIndex) {
    1167             :         // The entire entry is contained in the selection. Mark the
    1168             :         // entry invalid.
    1169           0 :         entry->mIsValid = false;
    1170             :       }
    1171             :     }
    1172             : 
    1173             :   //**** KDEBUG ****
    1174             :   // printf("\n---- Middle Delete\n");
    1175             :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1176             :   // PrintOffsetTable();
    1177             :   //**** KDEBUG ****
    1178             : 
    1179           0 :     if (i == mSelEndIndex) {
    1180           0 :       if (entry->mIsInsertedText) {
    1181             :         // Inserted text offset entries have no width when
    1182             :         // talking in terms of string offsets! If the end
    1183             :         // of the selection is in an inserted text offset entry,
    1184             :         // the selection includes the entire entry!
    1185             : 
    1186           0 :         entry->mIsValid = false;
    1187             :       } else {
    1188             :         // Calculate the length of the selection. Note that the
    1189             :         // selection length can be zero if the end of the selection
    1190             :         // is at the very beginning of a text node entry.
    1191             : 
    1192           0 :         selLength = mSelEndOffset - entry->mStrOffset;
    1193             : 
    1194           0 :         if (selLength > 0 &&
    1195           0 :             mSelEndOffset < entry->mStrOffset + entry->mLength) {
    1196             :           // mStrOffset is guaranteed to be inside the selection, even
    1197             :           // when mSelStartIndex == mSelEndIndex.
    1198             : 
    1199           0 :           nsresult rv = SplitOffsetEntry(i, entry->mLength - selLength);
    1200             : 
    1201           0 :           if (NS_FAILED(rv)) {
    1202             :             UNLOCK_DOC(this);
    1203           0 :             return rv;
    1204             :           }
    1205             : 
    1206             :           // Update the entry fields:
    1207             : 
    1208           0 :           newEntry = mOffsetTable[i+1];
    1209           0 :           newEntry->mNodeOffset = entry->mNodeOffset;
    1210             :         }
    1211             : 
    1212             : 
    1213           0 :         if (selLength > 0 &&
    1214           0 :             mSelEndOffset == entry->mStrOffset + entry->mLength) {
    1215             :           // The entire entry is contained in the selection. Mark the
    1216             :           // entry invalid.
    1217           0 :           entry->mIsValid = false;
    1218             :         }
    1219             :       }
    1220             :     }
    1221             : 
    1222           0 :     if (i != mSelStartIndex && i != mSelEndIndex) {
    1223             :       // The entire entry is contained in the selection. Mark the
    1224             :       // entry invalid.
    1225           0 :       entry->mIsValid = false;
    1226             :     }
    1227             :   }
    1228             : 
    1229             :   // Make sure mIterator always points to something valid!
    1230             : 
    1231           0 :   AdjustContentIterator();
    1232             : 
    1233             :   // Now delete the actual content!
    1234             : 
    1235             :   nsresult rv =
    1236           0 :     editor->DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
    1237             : 
    1238           0 :   if (NS_FAILED(rv)) {
    1239             :     UNLOCK_DOC(this);
    1240           0 :     return rv;
    1241             :   }
    1242             : 
    1243             :   // Now that we've actually deleted the selected content,
    1244             :   // check to see if our mExtent has changed, if so, then
    1245             :   // we have to create a new content iterator!
    1246             : 
    1247           0 :   if (origStartNode && origEndNode) {
    1248           0 :     nsCOMPtr<nsIDOMNode> curStartNode, curEndNode;
    1249           0 :     int32_t curStartOffset = 0, curEndOffset = 0;
    1250             : 
    1251           0 :     rv = GetRangeEndPoints(mExtent,
    1252           0 :                            getter_AddRefs(curStartNode), &curStartOffset,
    1253           0 :                            getter_AddRefs(curEndNode), &curEndOffset);
    1254             : 
    1255           0 :     if (NS_FAILED(rv)) {
    1256             :       UNLOCK_DOC(this);
    1257           0 :       return rv;
    1258             :     }
    1259             : 
    1260           0 :     if (origStartNode != curStartNode || origEndNode != curEndNode) {
    1261             :       // The range has changed, so we need to create a new content
    1262             :       // iterator based on the new range.
    1263             : 
    1264           0 :       nsCOMPtr<nsIContent> curContent;
    1265             : 
    1266           0 :       if (mIteratorStatus != nsTextServicesDocument::eIsDone) {
    1267             :         // The old iterator is still pointing to something valid,
    1268             :         // so get its current node so we can restore it after we
    1269             :         // create the new iterator!
    1270             : 
    1271           0 :         curContent = mIterator->GetCurrentNode()
    1272           0 :                      ? mIterator->GetCurrentNode()->AsContent()
    1273           0 :                      : nullptr;
    1274             :       }
    1275             : 
    1276             :       // Create the new iterator.
    1277             : 
    1278           0 :       rv = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
    1279             : 
    1280           0 :       if (NS_FAILED(rv)) {
    1281             :         UNLOCK_DOC(this);
    1282           0 :         return rv;
    1283             :       }
    1284             : 
    1285             :       // Now make the new iterator point to the content node
    1286             :       // the old one was pointing at.
    1287             : 
    1288           0 :       if (curContent) {
    1289           0 :         rv = mIterator->PositionAt(curContent);
    1290             : 
    1291           0 :         if (NS_FAILED(rv)) {
    1292           0 :           mIteratorStatus = eIsDone;
    1293             :         } else {
    1294           0 :           mIteratorStatus = eValid;
    1295             :         }
    1296             :       }
    1297             :     }
    1298             :   }
    1299             : 
    1300           0 :   entry = 0;
    1301             : 
    1302             :   // Move the caret to the end of the first valid entry.
    1303             :   // Start with mSelStartIndex since it may still be valid.
    1304             : 
    1305           0 :   for (int32_t i = mSelStartIndex; !entry && i >= 0; i--) {
    1306           0 :     entry = mOffsetTable[i];
    1307             : 
    1308           0 :     if (!entry->mIsValid) {
    1309           0 :       entry = 0;
    1310             :     } else {
    1311           0 :       mSelStartIndex  = mSelEndIndex  = i;
    1312           0 :       mSelStartOffset = mSelEndOffset = entry->mStrOffset + entry->mLength;
    1313             :     }
    1314             :   }
    1315             : 
    1316             :   // If we still don't have a valid entry, move the caret
    1317             :   // to the next valid entry after the selection:
    1318             : 
    1319           0 :   for (int32_t i = mSelEndIndex;
    1320           0 :        !entry && i < static_cast<int32_t>(mOffsetTable.Length()); i++) {
    1321           0 :     entry = mOffsetTable[i];
    1322             : 
    1323           0 :     if (!entry->mIsValid) {
    1324           0 :       entry = 0;
    1325             :     } else {
    1326           0 :       mSelStartIndex = mSelEndIndex = i;
    1327           0 :       mSelStartOffset = mSelEndOffset = entry->mStrOffset;
    1328             :     }
    1329             :   }
    1330             : 
    1331           0 :   if (entry) {
    1332           0 :     SetSelection(mSelStartOffset, 0);
    1333             :   } else {
    1334             :     // Uuughh we have no valid offset entry to place our
    1335             :     // caret ... just mark the selection invalid.
    1336           0 :     mSelStartIndex  = mSelEndIndex  = -1;
    1337           0 :     mSelStartOffset = mSelEndOffset = -1;
    1338             :   }
    1339             : 
    1340             :   // Now remove any invalid entries from the offset table.
    1341             : 
    1342           0 :   rv = RemoveInvalidOffsetEntries();
    1343             : 
    1344             :   //**** KDEBUG ****
    1345             :   // printf("\n---- After Delete\n");
    1346             :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1347             :   // PrintOffsetTable();
    1348             :   //**** KDEBUG ****
    1349             : 
    1350             :   UNLOCK_DOC(this);
    1351             : 
    1352           0 :   return rv;
    1353             : }
    1354             : 
    1355             : NS_IMETHODIMP
    1356           0 : nsTextServicesDocument::InsertText(const nsString *aText)
    1357             : {
    1358           0 :   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
    1359           0 :   NS_ASSERTION(editor, "InsertText called without an editor present!");
    1360             : 
    1361           0 :   if (!editor || !SelectionIsValid()) {
    1362           0 :     return NS_ERROR_FAILURE;
    1363             :   }
    1364             : 
    1365           0 :   NS_ENSURE_TRUE(aText, NS_ERROR_NULL_POINTER);
    1366             : 
    1367             :   // If the selection is not collapsed, we need to save
    1368             :   // off the selection offsets so we can restore the
    1369             :   // selection and delete the selected content after we've
    1370             :   // inserted the new text. This is necessary to try and
    1371             :   // retain as much of the original style of the content
    1372             :   // being deleted.
    1373             : 
    1374           0 :   bool collapsedSelection = SelectionIsCollapsed();
    1375           0 :   int32_t savedSelOffset = mSelStartOffset;
    1376           0 :   int32_t savedSelLength = mSelEndOffset - mSelStartOffset;
    1377             : 
    1378           0 :   if (!collapsedSelection) {
    1379             :     // Collapse to the start of the current selection
    1380             :     // for the insert!
    1381             : 
    1382           0 :     nsresult rv = SetSelection(mSelStartOffset, 0);
    1383             : 
    1384           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1385             :   }
    1386             : 
    1387             : 
    1388             :   LOCK_DOC(this);
    1389             : 
    1390           0 :   nsresult rv = editor->BeginTransaction();
    1391             : 
    1392           0 :   if (NS_FAILED(rv)) {
    1393             :     UNLOCK_DOC(this);
    1394           0 :     return rv;
    1395             :   }
    1396             : 
    1397           0 :   nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(editor, &rv));
    1398           0 :   if (textEditor) {
    1399           0 :     rv = textEditor->InsertText(*aText);
    1400             :   }
    1401             : 
    1402           0 :   if (NS_FAILED(rv)) {
    1403           0 :     editor->EndTransaction();
    1404             :     UNLOCK_DOC(this);
    1405           0 :     return rv;
    1406             :   }
    1407             : 
    1408             :   //**** KDEBUG ****
    1409             :   // printf("\n---- Before Insert\n");
    1410             :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1411             :   // PrintOffsetTable();
    1412             :   //**** KDEBUG ****
    1413             : 
    1414           0 :   int32_t strLength = aText->Length();
    1415             : 
    1416           0 :   nsCOMPtr<nsISelection> selection;
    1417             :   OffsetEntry *itEntry;
    1418           0 :   OffsetEntry *entry = mOffsetTable[mSelStartIndex];
    1419           0 :   void *node         = entry->mNode;
    1420             : 
    1421           0 :   NS_ASSERTION((entry->mIsValid), "Invalid insertion point!");
    1422             : 
    1423           0 :   if (entry->mStrOffset == mSelStartOffset) {
    1424           0 :     if (entry->mIsInsertedText) {
    1425             :       // If the caret is in an inserted text offset entry,
    1426             :       // we simply insert the text at the end of the entry.
    1427             : 
    1428           0 :       entry->mLength += strLength;
    1429             :     } else {
    1430             :       // Insert an inserted text offset entry before the current
    1431             :       // entry!
    1432             : 
    1433           0 :       itEntry = new OffsetEntry(entry->mNode, entry->mStrOffset, strLength);
    1434             : 
    1435           0 :       if (!itEntry) {
    1436           0 :         editor->EndTransaction();
    1437             :         UNLOCK_DOC(this);
    1438           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1439             :       }
    1440             : 
    1441           0 :       itEntry->mIsInsertedText = true;
    1442           0 :       itEntry->mNodeOffset = entry->mNodeOffset;
    1443             : 
    1444           0 :       if (!mOffsetTable.InsertElementAt(mSelStartIndex, itEntry)) {
    1445           0 :         editor->EndTransaction();
    1446             :         UNLOCK_DOC(this);
    1447           0 :         return NS_ERROR_FAILURE;
    1448             :       }
    1449             :     }
    1450           0 :   } else if (entry->mStrOffset + entry->mLength == mSelStartOffset) {
    1451             :     // We are inserting text at the end of the current offset entry.
    1452             :     // Look at the next valid entry in the table. If it's an inserted
    1453             :     // text entry, add to its length and adjust its node offset. If
    1454             :     // it isn't, add a new inserted text entry.
    1455             : 
    1456             :     // XXX Rename this!
    1457           0 :     uint32_t i = mSelStartIndex + 1;
    1458           0 :     itEntry = 0;
    1459             : 
    1460           0 :     if (mOffsetTable.Length() > i) {
    1461           0 :       itEntry = mOffsetTable[i];
    1462             : 
    1463           0 :       if (!itEntry) {
    1464           0 :         editor->EndTransaction();
    1465             :         UNLOCK_DOC(this);
    1466           0 :         return NS_ERROR_FAILURE;
    1467             :       }
    1468             : 
    1469             :       // Check if the entry is a match. If it isn't, set
    1470             :       // iEntry to zero.
    1471             : 
    1472           0 :       if (!itEntry->mIsInsertedText || itEntry->mStrOffset != mSelStartOffset) {
    1473           0 :         itEntry = 0;
    1474             :       }
    1475             :     }
    1476             : 
    1477           0 :     if (!itEntry) {
    1478             :       // We didn't find an inserted text offset entry, so
    1479             :       // create one.
    1480             : 
    1481           0 :       itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, 0);
    1482             : 
    1483           0 :       if (!itEntry) {
    1484           0 :         editor->EndTransaction();
    1485             :         UNLOCK_DOC(this);
    1486           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1487             :       }
    1488             : 
    1489           0 :       itEntry->mNodeOffset = entry->mNodeOffset + entry->mLength;
    1490           0 :       itEntry->mIsInsertedText = true;
    1491             : 
    1492           0 :       if (!mOffsetTable.InsertElementAt(i, itEntry)) {
    1493           0 :         delete itEntry;
    1494           0 :         return NS_ERROR_FAILURE;
    1495             :       }
    1496             :     }
    1497             : 
    1498             :     // We have a valid inserted text offset entry. Update its
    1499             :     // length, adjust the selection indexes, and make sure the
    1500             :     // caret is properly placed!
    1501             : 
    1502           0 :     itEntry->mLength += strLength;
    1503             : 
    1504           0 :     mSelStartIndex = mSelEndIndex = i;
    1505             : 
    1506           0 :     rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
    1507           0 :                                getter_AddRefs(selection));
    1508             : 
    1509           0 :     if (NS_FAILED(rv)) {
    1510           0 :       editor->EndTransaction();
    1511             :       UNLOCK_DOC(this);
    1512           0 :       return rv;
    1513             :     }
    1514             : 
    1515           0 :     rv = selection->Collapse(itEntry->mNode,
    1516           0 :                              itEntry->mNodeOffset + itEntry->mLength);
    1517             : 
    1518           0 :     if (NS_FAILED(rv)) {
    1519           0 :       editor->EndTransaction();
    1520             :       UNLOCK_DOC(this);
    1521           0 :       return rv;
    1522             :     }
    1523           0 :   } else if (entry->mStrOffset + entry->mLength > mSelStartOffset) {
    1524             :     // We are inserting text into the middle of the current offset entry.
    1525             :     // split the current entry into two parts, then insert an inserted text
    1526             :     // entry between them!
    1527             : 
    1528             :     // XXX Rename this!
    1529           0 :     uint32_t i = entry->mLength - (mSelStartOffset - entry->mStrOffset);
    1530             : 
    1531           0 :     rv = SplitOffsetEntry(mSelStartIndex, i);
    1532             : 
    1533           0 :     if (NS_FAILED(rv)) {
    1534           0 :       editor->EndTransaction();
    1535             :       UNLOCK_DOC(this);
    1536           0 :       return rv;
    1537             :     }
    1538             : 
    1539           0 :     itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, strLength);
    1540             : 
    1541           0 :     if (!itEntry) {
    1542           0 :       editor->EndTransaction();
    1543             :       UNLOCK_DOC(this);
    1544           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1545             :     }
    1546             : 
    1547           0 :     itEntry->mIsInsertedText = true;
    1548           0 :     itEntry->mNodeOffset     = entry->mNodeOffset + entry->mLength;
    1549             : 
    1550           0 :     if (!mOffsetTable.InsertElementAt(mSelStartIndex + 1, itEntry)) {
    1551           0 :       editor->EndTransaction();
    1552             :       UNLOCK_DOC(this);
    1553           0 :       return NS_ERROR_FAILURE;
    1554             :     }
    1555             : 
    1556           0 :     mSelEndIndex = ++mSelStartIndex;
    1557             :   }
    1558             : 
    1559             :   // We've just finished inserting an inserted text offset entry.
    1560             :   // update all entries with the same mNode pointer that follow
    1561             :   // it in the table!
    1562             : 
    1563           0 :   for (size_t i = mSelStartIndex + 1; i < mOffsetTable.Length(); i++) {
    1564           0 :     entry = mOffsetTable[i];
    1565           0 :     if (entry->mNode != node) {
    1566           0 :       break;
    1567             :     }
    1568           0 :     if (entry->mIsValid) {
    1569           0 :       entry->mNodeOffset += strLength;
    1570             :     }
    1571             :   }
    1572             : 
    1573             :   //**** KDEBUG ****
    1574             :   // printf("\n---- After Insert\n");
    1575             :   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    1576             :   // PrintOffsetTable();
    1577             :   //**** KDEBUG ****
    1578             : 
    1579           0 :   if (!collapsedSelection) {
    1580           0 :     rv = SetSelection(savedSelOffset, savedSelLength);
    1581             : 
    1582           0 :     if (NS_FAILED(rv)) {
    1583           0 :       editor->EndTransaction();
    1584             :       UNLOCK_DOC(this);
    1585           0 :       return rv;
    1586             :     }
    1587             : 
    1588           0 :     rv = DeleteSelection();
    1589             : 
    1590           0 :     if (NS_FAILED(rv)) {
    1591           0 :       editor->EndTransaction();
    1592             :       UNLOCK_DOC(this);
    1593           0 :       return rv;
    1594             :     }
    1595             :   }
    1596             : 
    1597           0 :   rv = editor->EndTransaction();
    1598             : 
    1599             :   UNLOCK_DOC(this);
    1600             : 
    1601           0 :   return rv;
    1602             : }
    1603             : 
    1604             : NS_IMETHODIMP
    1605           0 : nsTextServicesDocument::DidInsertNode(nsIDOMNode *aNode,
    1606             :                                       nsIDOMNode *aParent,
    1607             :                                       int32_t     aPosition,
    1608             :                                       nsresult    aResult)
    1609             : {
    1610           0 :   return NS_OK;
    1611             : }
    1612             : 
    1613             : NS_IMETHODIMP
    1614           0 : nsTextServicesDocument::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
    1615             : {
    1616           0 :   NS_ENSURE_SUCCESS(aResult, NS_OK);
    1617             : 
    1618           0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
    1619             : 
    1620             :   //**** KDEBUG ****
    1621             :   // printf("** DeleteNode: 0x%.8x\n", aChild);
    1622             :   // fflush(stdout);
    1623             :   //**** KDEBUG ****
    1624             : 
    1625             :   LOCK_DOC(this);
    1626             : 
    1627           0 :   int32_t nodeIndex = 0;
    1628           0 :   bool hasEntry = false;
    1629             :   OffsetEntry *entry;
    1630             : 
    1631             :   nsresult rv =
    1632           0 :     NodeHasOffsetEntry(&mOffsetTable, aChild, &hasEntry, &nodeIndex);
    1633             : 
    1634           0 :   if (NS_FAILED(rv)) {
    1635             :     UNLOCK_DOC(this);
    1636           0 :     return rv;
    1637             :   }
    1638             : 
    1639           0 :   if (!hasEntry) {
    1640             :     // It's okay if the node isn't in the offset table, the
    1641             :     // editor could be cleaning house.
    1642             :     UNLOCK_DOC(this);
    1643           0 :     return NS_OK;
    1644             :   }
    1645             : 
    1646           0 :   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mIterator->GetCurrentNode());
    1647             : 
    1648           0 :   if (node && node == aChild &&
    1649           0 :       mIteratorStatus != nsTextServicesDocument::eIsDone) {
    1650             :     // XXX: This should never really happen because
    1651             :     // AdjustContentIterator() should have been called prior
    1652             :     // to the delete to try and position the iterator on the
    1653             :     // next valid text node in the offset table, and if there
    1654             :     // wasn't a next, it would've set mIteratorStatus to eIsDone.
    1655             : 
    1656           0 :     NS_ERROR("DeleteNode called for current iterator node.");
    1657             :   }
    1658             : 
    1659           0 :   int32_t tcount = mOffsetTable.Length();
    1660             : 
    1661           0 :   while (nodeIndex < tcount) {
    1662           0 :     entry = mOffsetTable[nodeIndex];
    1663             : 
    1664           0 :     if (!entry) {
    1665             :       UNLOCK_DOC(this);
    1666           0 :       return NS_ERROR_FAILURE;
    1667             :     }
    1668             : 
    1669           0 :     if (entry->mNode == aChild) {
    1670           0 :       entry->mIsValid = false;
    1671             :     }
    1672             : 
    1673           0 :     nodeIndex++;
    1674             :   }
    1675             : 
    1676             :   UNLOCK_DOC(this);
    1677             : 
    1678           0 :   return NS_OK;
    1679             : }
    1680             : 
    1681             : NS_IMETHODIMP
    1682           0 : nsTextServicesDocument::DidSplitNode(nsIDOMNode *aExistingRightNode,
    1683             :                                      int32_t     aOffset,
    1684             :                                      nsIDOMNode *aNewLeftNode,
    1685             :                                      nsresult    aResult)
    1686             : {
    1687             :   //**** KDEBUG ****
    1688             :   // printf("** SplitNode: 0x%.8x  %d  0x%.8x\n", aExistingRightNode, aOffset, aNewLeftNode);
    1689             :   // fflush(stdout);
    1690             :   //**** KDEBUG ****
    1691           0 :   return NS_OK;
    1692             : }
    1693             : 
    1694             : NS_IMETHODIMP
    1695           0 : nsTextServicesDocument::DidJoinNodes(nsIDOMNode  *aLeftNode,
    1696             :                                      nsIDOMNode  *aRightNode,
    1697             :                                      nsIDOMNode  *aParent,
    1698             :                                      nsresult     aResult)
    1699             : {
    1700           0 :   NS_ENSURE_SUCCESS(aResult, NS_OK);
    1701             : 
    1702             :   //**** KDEBUG ****
    1703             :   // printf("** JoinNodes: 0x%.8x  0x%.8x  0x%.8x\n", aLeftNode, aRightNode, aParent);
    1704             :   // fflush(stdout);
    1705             :   //**** KDEBUG ****
    1706             : 
    1707             :   // Make sure that both nodes are text nodes -- otherwise we don't care.
    1708             : 
    1709             :   uint16_t type;
    1710           0 :   nsresult rv = aLeftNode->GetNodeType(&type);
    1711           0 :   NS_ENSURE_SUCCESS(rv, NS_OK);
    1712           0 :   if (nsIDOMNode::TEXT_NODE != type) {
    1713           0 :     return NS_OK;
    1714             :   }
    1715             : 
    1716           0 :   rv = aRightNode->GetNodeType(&type);
    1717           0 :   NS_ENSURE_SUCCESS(rv, NS_OK);
    1718           0 :   if (nsIDOMNode::TEXT_NODE != type) {
    1719           0 :     return NS_OK;
    1720             :   }
    1721             : 
    1722             :   // Note: The editor merges the contents of the left node into the
    1723             :   //       contents of the right.
    1724             : 
    1725           0 :   int32_t leftIndex = 0;
    1726           0 :   int32_t rightIndex = 0;
    1727           0 :   bool leftHasEntry = false;
    1728           0 :   bool rightHasEntry = false;
    1729             : 
    1730           0 :   rv = NodeHasOffsetEntry(&mOffsetTable, aLeftNode, &leftHasEntry, &leftIndex);
    1731             : 
    1732           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1733             : 
    1734           0 :   if (!leftHasEntry) {
    1735             :     // It's okay if the node isn't in the offset table, the
    1736             :     // editor could be cleaning house.
    1737           0 :     return NS_OK;
    1738             :   }
    1739             : 
    1740           0 :   rv = NodeHasOffsetEntry(&mOffsetTable, aRightNode,
    1741           0 :                           &rightHasEntry, &rightIndex);
    1742             : 
    1743           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1744             : 
    1745           0 :   if (!rightHasEntry) {
    1746             :     // It's okay if the node isn't in the offset table, the
    1747             :     // editor could be cleaning house.
    1748           0 :     return NS_OK;
    1749             :   }
    1750             : 
    1751           0 :   NS_ASSERTION(leftIndex < rightIndex, "Indexes out of order.");
    1752             : 
    1753           0 :   if (leftIndex > rightIndex) {
    1754             :     // Don't know how to handle this situation.
    1755           0 :     return NS_ERROR_FAILURE;
    1756             :   }
    1757             : 
    1758             :   LOCK_DOC(this);
    1759             : 
    1760           0 :   OffsetEntry *entry = mOffsetTable[rightIndex];
    1761           0 :   NS_ASSERTION(entry->mNodeOffset == 0, "Unexpected offset value for rightIndex.");
    1762             : 
    1763             :   // Run through the table and change all entries referring to
    1764             :   // the left node so that they now refer to the right node:
    1765             : 
    1766           0 :   nsAutoString str;
    1767           0 :   aLeftNode->GetNodeValue(str);
    1768           0 :   int32_t nodeLength = str.Length();
    1769             : 
    1770           0 :   for (int32_t i = leftIndex; i < rightIndex; i++) {
    1771           0 :     entry = mOffsetTable[i];
    1772           0 :     if (entry->mNode != aLeftNode) {
    1773           0 :       break;
    1774             :     }
    1775           0 :     if (entry->mIsValid) {
    1776           0 :       entry->mNode = aRightNode;
    1777             :     }
    1778             :   }
    1779             : 
    1780             :   // Run through the table and adjust the node offsets
    1781             :   // for all entries referring to the right node.
    1782             : 
    1783           0 :   for (int32_t i = rightIndex;
    1784           0 :        i < static_cast<int32_t>(mOffsetTable.Length()); i++) {
    1785           0 :     entry = mOffsetTable[i];
    1786           0 :     if (entry->mNode != aRightNode) {
    1787           0 :       break;
    1788             :     }
    1789           0 :     if (entry->mIsValid) {
    1790           0 :       entry->mNodeOffset += nodeLength;
    1791             :     }
    1792             :   }
    1793             : 
    1794             :   // Now check to see if the iterator is pointing to the
    1795             :   // left node. If it is, make it point to the right node!
    1796             : 
    1797           0 :   nsCOMPtr<nsIContent> leftContent = do_QueryInterface(aLeftNode);
    1798           0 :   nsCOMPtr<nsIContent> rightContent = do_QueryInterface(aRightNode);
    1799             : 
    1800           0 :   if (!leftContent || !rightContent) {
    1801             :     UNLOCK_DOC(this);
    1802           0 :     return NS_ERROR_FAILURE;
    1803             :   }
    1804             : 
    1805           0 :   if (mIterator->GetCurrentNode() == leftContent) {
    1806           0 :     mIterator->PositionAt(rightContent);
    1807             :   }
    1808             : 
    1809             :   UNLOCK_DOC(this);
    1810             : 
    1811           0 :   return NS_OK;
    1812             : }
    1813             : 
    1814             : nsresult
    1815           0 : nsTextServicesDocument::CreateContentIterator(nsRange* aRange,
    1816             :                                               nsIContentIterator** aIterator)
    1817             : {
    1818           0 :   NS_ENSURE_TRUE(aRange && aIterator, NS_ERROR_NULL_POINTER);
    1819             : 
    1820           0 :   *aIterator = nullptr;
    1821             : 
    1822             :   // Create a nsFilteredContentIterator
    1823             :   // This class wraps the ContentIterator in order to give itself a chance
    1824             :   // to filter out certain content nodes
    1825           0 :   RefPtr<nsFilteredContentIterator> filter = new nsFilteredContentIterator(mTxtSvcFilter);
    1826             : 
    1827           0 :   nsresult rv = filter->Init(aRange);
    1828           0 :   if (NS_FAILED(rv)) {
    1829           0 :     return rv;
    1830             :   }
    1831             : 
    1832           0 :   filter.forget(aIterator);
    1833           0 :   return NS_OK;
    1834             : }
    1835             : 
    1836             : nsresult
    1837           0 : nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode **aNode)
    1838             : {
    1839           0 :   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
    1840             : 
    1841           0 :   *aNode = 0;
    1842             : 
    1843           0 :   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
    1844             : 
    1845           0 :   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDOMDocument);
    1846             : 
    1847           0 :   if (htmlDoc) {
    1848             :     // For HTML documents, the content root node is the body.
    1849             : 
    1850           0 :     nsCOMPtr<nsIDOMHTMLElement> bodyElement;
    1851             : 
    1852           0 :     nsresult rv = htmlDoc->GetBody(getter_AddRefs(bodyElement));
    1853             : 
    1854           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1855             : 
    1856           0 :     NS_ENSURE_TRUE(bodyElement, NS_ERROR_FAILURE);
    1857             : 
    1858           0 :     bodyElement.forget(aNode);
    1859             :   } else {
    1860             :     // For non-HTML documents, the content root node will be the document element.
    1861             : 
    1862           0 :     nsCOMPtr<nsIDOMElement> docElement;
    1863             : 
    1864           0 :     nsresult rv = mDOMDocument->GetDocumentElement(getter_AddRefs(docElement));
    1865             : 
    1866           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1867             : 
    1868           0 :     NS_ENSURE_TRUE(docElement, NS_ERROR_FAILURE);
    1869             : 
    1870           0 :     docElement.forget(aNode);
    1871             :   }
    1872             : 
    1873           0 :   return NS_OK;
    1874             : }
    1875             : 
    1876             : nsresult
    1877           0 : nsTextServicesDocument::CreateDocumentContentRange(nsRange** aRange)
    1878             : {
    1879           0 :   *aRange = nullptr;
    1880             : 
    1881           0 :   nsCOMPtr<nsIDOMNode> node;
    1882           0 :   nsresult rv = GetDocumentContentRootNode(getter_AddRefs(node));
    1883           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1884           0 :   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
    1885             : 
    1886           0 :   nsCOMPtr<nsINode> nativeNode = do_QueryInterface(node);
    1887           0 :   NS_ENSURE_STATE(nativeNode);
    1888             : 
    1889           0 :   RefPtr<nsRange> range = new nsRange(nativeNode);
    1890             : 
    1891           0 :   rv = range->SelectNodeContents(node);
    1892           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1893             : 
    1894           0 :   range.forget(aRange);
    1895           0 :   return NS_OK;
    1896             : }
    1897             : 
    1898             : nsresult
    1899           0 : nsTextServicesDocument::CreateDocumentContentRootToNodeOffsetRange(
    1900             :     nsIDOMNode* aParent, int32_t aOffset, bool aToStart, nsRange** aRange)
    1901             : {
    1902           0 :   NS_ENSURE_TRUE(aParent && aRange, NS_ERROR_NULL_POINTER);
    1903             : 
    1904           0 :   *aRange = 0;
    1905             : 
    1906           0 :   NS_ASSERTION(aOffset >= 0, "Invalid offset!");
    1907             : 
    1908           0 :   if (aOffset < 0) {
    1909           0 :     return NS_ERROR_FAILURE;
    1910             :   }
    1911             : 
    1912           0 :   nsCOMPtr<nsIDOMNode> bodyNode;
    1913           0 :   nsresult rv = GetDocumentContentRootNode(getter_AddRefs(bodyNode));
    1914           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1915           0 :   NS_ENSURE_TRUE(bodyNode, NS_ERROR_NULL_POINTER);
    1916             : 
    1917           0 :   nsCOMPtr<nsIDOMNode> startNode;
    1918           0 :   nsCOMPtr<nsIDOMNode> endNode;
    1919             :   int32_t startOffset, endOffset;
    1920             : 
    1921           0 :   if (aToStart) {
    1922             :     // The range should begin at the start of the document
    1923             :     // and extend up until (aParent, aOffset).
    1924             : 
    1925           0 :     startNode   = bodyNode;
    1926           0 :     startOffset = 0;
    1927           0 :     endNode     = aParent;
    1928           0 :     endOffset   = aOffset;
    1929             :   } else {
    1930             :     // The range should begin at (aParent, aOffset) and
    1931             :     // extend to the end of the document.
    1932             : 
    1933           0 :     startNode   = aParent;
    1934           0 :     startOffset = aOffset;
    1935           0 :     endNode     = bodyNode;
    1936             : 
    1937           0 :     nsCOMPtr<nsINode> body = do_QueryInterface(bodyNode);
    1938           0 :     endOffset = body ? int32_t(body->GetChildCount()) : 0;
    1939             :   }
    1940             : 
    1941           0 :   return nsRange::CreateRange(startNode, startOffset, endNode, endOffset,
    1942           0 :                               aRange);
    1943             : }
    1944             : 
    1945             : nsresult
    1946           0 : nsTextServicesDocument::CreateDocumentContentIterator(nsIContentIterator **aIterator)
    1947             : {
    1948           0 :   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
    1949             : 
    1950           0 :   RefPtr<nsRange> range;
    1951             : 
    1952           0 :   nsresult rv = CreateDocumentContentRange(getter_AddRefs(range));
    1953             : 
    1954           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1955             : 
    1956           0 :   return CreateContentIterator(range, aIterator);
    1957             : }
    1958             : 
    1959             : nsresult
    1960           0 : nsTextServicesDocument::AdjustContentIterator()
    1961             : {
    1962           0 :   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
    1963             : 
    1964           0 :   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mIterator->GetCurrentNode()));
    1965             : 
    1966           0 :   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    1967             : 
    1968           0 :   nsIDOMNode *nodePtr = node.get();
    1969           0 :   int32_t tcount      = mOffsetTable.Length();
    1970             : 
    1971           0 :   nsIDOMNode *prevValidNode = 0;
    1972           0 :   nsIDOMNode *nextValidNode = 0;
    1973           0 :   bool foundEntry = false;
    1974             :   OffsetEntry *entry;
    1975             : 
    1976           0 :   for (int32_t i = 0; i < tcount && !nextValidNode; i++) {
    1977           0 :     entry = mOffsetTable[i];
    1978             : 
    1979           0 :     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    1980             : 
    1981           0 :     if (entry->mNode == nodePtr) {
    1982           0 :       if (entry->mIsValid) {
    1983             :         // The iterator is still pointing to something valid!
    1984             :         // Do nothing!
    1985           0 :         return NS_OK;
    1986             :       }
    1987             :       // We found an invalid entry that points to
    1988             :       // the current iterator node. Stop looking for
    1989             :       // a previous valid node!
    1990           0 :       foundEntry = true;
    1991             :     }
    1992             : 
    1993           0 :     if (entry->mIsValid) {
    1994           0 :       if (!foundEntry) {
    1995           0 :         prevValidNode = entry->mNode;
    1996             :       } else {
    1997           0 :         nextValidNode = entry->mNode;
    1998             :       }
    1999             :     }
    2000             :   }
    2001             : 
    2002           0 :   nsCOMPtr<nsIContent> content;
    2003             : 
    2004           0 :   if (prevValidNode) {
    2005           0 :     content = do_QueryInterface(prevValidNode);
    2006           0 :   } else if (nextValidNode) {
    2007           0 :     content = do_QueryInterface(nextValidNode);
    2008             :   }
    2009             : 
    2010           0 :   if (content) {
    2011           0 :     nsresult rv = mIterator->PositionAt(content);
    2012             : 
    2013           0 :     if (NS_FAILED(rv)) {
    2014           0 :       mIteratorStatus = eIsDone;
    2015             :     } else {
    2016           0 :       mIteratorStatus = eValid;
    2017             :     }
    2018           0 :     return rv;
    2019             :   }
    2020             : 
    2021             :   // If we get here, there aren't any valid entries
    2022             :   // in the offset table! Try to position the iterator
    2023             :   // on the next text block first, then previous if
    2024             :   // one doesn't exist!
    2025             : 
    2026           0 :   if (mNextTextBlock) {
    2027           0 :     nsresult rv = mIterator->PositionAt(mNextTextBlock);
    2028             : 
    2029           0 :     if (NS_FAILED(rv)) {
    2030           0 :       mIteratorStatus = eIsDone;
    2031           0 :       return rv;
    2032             :     }
    2033             : 
    2034           0 :     mIteratorStatus = eNext;
    2035           0 :   } else if (mPrevTextBlock) {
    2036           0 :     nsresult rv = mIterator->PositionAt(mPrevTextBlock);
    2037             : 
    2038           0 :     if (NS_FAILED(rv)) {
    2039           0 :       mIteratorStatus = eIsDone;
    2040           0 :       return rv;
    2041             :     }
    2042             : 
    2043           0 :     mIteratorStatus = ePrev;
    2044             :   } else {
    2045           0 :     mIteratorStatus = eIsDone;
    2046             :   }
    2047           0 :   return NS_OK;
    2048             : }
    2049             : 
    2050             : bool
    2051           0 : nsTextServicesDocument::DidSkip(nsIContentIterator* aFilteredIter)
    2052             : {
    2053             :   // We can assume here that the Iterator is a nsFilteredContentIterator because
    2054             :   // all the iterator are created in CreateContentIterator which create a
    2055             :   // nsFilteredContentIterator
    2056             :   // So if the iterator bailed on one of the "filtered" content nodes then we
    2057             :   // consider that to be a block and bail with true
    2058           0 :   if (aFilteredIter) {
    2059           0 :     nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
    2060           0 :     if (filter && filter->DidSkip()) {
    2061           0 :       return true;
    2062             :     }
    2063             :   }
    2064           0 :   return false;
    2065             : }
    2066             : 
    2067             : void
    2068           0 : nsTextServicesDocument::ClearDidSkip(nsIContentIterator* aFilteredIter)
    2069             : {
    2070             :   // Clear filter's skip flag
    2071           0 :   if (aFilteredIter) {
    2072           0 :     nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
    2073           0 :     filter->ClearDidSkip();
    2074             :   }
    2075           0 : }
    2076             : 
    2077             : bool
    2078           0 : nsTextServicesDocument::IsBlockNode(nsIContent *aContent)
    2079             : {
    2080           0 :   if (!aContent) {
    2081           0 :     NS_ERROR("How did a null pointer get passed to IsBlockNode?");
    2082           0 :     return false;
    2083             :   }
    2084             : 
    2085           0 :   nsIAtom *atom = aContent->NodeInfo()->NameAtom();
    2086             : 
    2087           0 :   return (sAAtom       != atom &&
    2088           0 :           sAddressAtom != atom &&
    2089           0 :           sBigAtom     != atom &&
    2090           0 :           sBAtom       != atom &&
    2091           0 :           sCiteAtom    != atom &&
    2092           0 :           sCodeAtom    != atom &&
    2093           0 :           sDfnAtom     != atom &&
    2094           0 :           sEmAtom      != atom &&
    2095           0 :           sFontAtom    != atom &&
    2096           0 :           sIAtom       != atom &&
    2097           0 :           sKbdAtom     != atom &&
    2098           0 :           sKeygenAtom  != atom &&
    2099           0 :           sNobrAtom    != atom &&
    2100           0 :           sSAtom       != atom &&
    2101           0 :           sSampAtom    != atom &&
    2102           0 :           sSmallAtom   != atom &&
    2103           0 :           sSpacerAtom  != atom &&
    2104           0 :           sSpanAtom    != atom &&
    2105           0 :           sStrikeAtom  != atom &&
    2106           0 :           sStrongAtom  != atom &&
    2107           0 :           sSubAtom     != atom &&
    2108           0 :           sSupAtom     != atom &&
    2109           0 :           sTtAtom      != atom &&
    2110           0 :           sUAtom       != atom &&
    2111           0 :           sVarAtom     != atom &&
    2112           0 :           sWbrAtom     != atom);
    2113             : }
    2114             : 
    2115             : bool
    2116           0 : nsTextServicesDocument::HasSameBlockNodeParent(nsIContent *aContent1, nsIContent *aContent2)
    2117             : {
    2118           0 :   nsIContent* p1 = aContent1->GetParent();
    2119           0 :   nsIContent* p2 = aContent2->GetParent();
    2120             : 
    2121             :   // Quick test:
    2122             : 
    2123           0 :   if (p1 == p2) {
    2124           0 :     return true;
    2125             :   }
    2126             : 
    2127             :   // Walk up the parent hierarchy looking for closest block boundary node:
    2128             : 
    2129           0 :   while (p1 && !IsBlockNode(p1)) {
    2130           0 :     p1 = p1->GetParent();
    2131             :   }
    2132             : 
    2133           0 :   while (p2 && !IsBlockNode(p2)) {
    2134           0 :     p2 = p2->GetParent();
    2135             :   }
    2136             : 
    2137           0 :   return p1 == p2;
    2138             : }
    2139             : 
    2140             : bool
    2141           0 : nsTextServicesDocument::IsTextNode(nsIContent *aContent)
    2142             : {
    2143           0 :   NS_ENSURE_TRUE(aContent, false);
    2144           0 :   return nsIDOMNode::TEXT_NODE == aContent->NodeType();
    2145             : }
    2146             : 
    2147             : bool
    2148           0 : nsTextServicesDocument::IsTextNode(nsIDOMNode *aNode)
    2149             : {
    2150           0 :   NS_ENSURE_TRUE(aNode, false);
    2151             : 
    2152           0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
    2153           0 :   return IsTextNode(content);
    2154             : }
    2155             : 
    2156             : nsresult
    2157           0 : nsTextServicesDocument::SetSelectionInternal(int32_t aOffset, int32_t aLength, bool aDoUpdate)
    2158             : {
    2159           0 :   NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
    2160             : 
    2161           0 :   nsIDOMNode *sNode = 0, *eNode = 0;
    2162           0 :   int32_t sOffset = 0, eOffset = 0;
    2163             :   OffsetEntry *entry;
    2164             : 
    2165             :   // Find start of selection in node offset terms:
    2166             : 
    2167           0 :   for (size_t i = 0; !sNode && i < mOffsetTable.Length(); i++) {
    2168           0 :     entry = mOffsetTable[i];
    2169           0 :     if (entry->mIsValid) {
    2170           0 :       if (entry->mIsInsertedText) {
    2171             :         // Caret can only be placed at the end of an
    2172             :         // inserted text offset entry, if the offsets
    2173             :         // match exactly!
    2174             : 
    2175           0 :         if (entry->mStrOffset == aOffset) {
    2176           0 :           sNode   = entry->mNode;
    2177           0 :           sOffset = entry->mNodeOffset + entry->mLength;
    2178             :         }
    2179           0 :       } else if (aOffset >= entry->mStrOffset) {
    2180           0 :         bool foundEntry = false;
    2181           0 :         int32_t strEndOffset = entry->mStrOffset + entry->mLength;
    2182             : 
    2183           0 :         if (aOffset < strEndOffset) {
    2184           0 :           foundEntry = true;
    2185           0 :         } else if (aOffset == strEndOffset) {
    2186             :           // Peek after this entry to see if we have any
    2187             :           // inserted text entries belonging to the same
    2188             :           // entry->mNode. If so, we have to place the selection
    2189             :           // after it!
    2190             : 
    2191           0 :           if (i + 1 < mOffsetTable.Length()) {
    2192           0 :             OffsetEntry *nextEntry = mOffsetTable[i+1];
    2193             : 
    2194           0 :             if (!nextEntry->mIsValid || nextEntry->mStrOffset != aOffset) {
    2195             :               // Next offset entry isn't an exact match, so we'll
    2196             :               // just use the current entry.
    2197           0 :               foundEntry = true;
    2198             :             }
    2199             :           }
    2200             :         }
    2201             : 
    2202           0 :         if (foundEntry) {
    2203           0 :           sNode   = entry->mNode;
    2204           0 :           sOffset = entry->mNodeOffset + aOffset - entry->mStrOffset;
    2205             :         }
    2206             :       }
    2207             : 
    2208           0 :       if (sNode) {
    2209           0 :         mSelStartIndex = static_cast<int32_t>(i);
    2210           0 :         mSelStartOffset = aOffset;
    2211             :       }
    2212             :     }
    2213             :   }
    2214             : 
    2215           0 :   NS_ENSURE_TRUE(sNode, NS_ERROR_FAILURE);
    2216             : 
    2217             :   // XXX: If we ever get a SetSelection() method in nsIEditor, we should
    2218             :   //      use it.
    2219             : 
    2220           0 :   nsCOMPtr<nsISelection> selection;
    2221             : 
    2222           0 :   if (aDoUpdate) {
    2223             :     nsresult rv =
    2224           0 :       mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
    2225           0 :                             getter_AddRefs(selection));
    2226             : 
    2227           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2228             : 
    2229           0 :     rv = selection->Collapse(sNode, sOffset);
    2230             : 
    2231           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2232             :    }
    2233             : 
    2234           0 :   if (aLength <= 0) {
    2235             :     // We have a collapsed selection. (Caret)
    2236             : 
    2237           0 :     mSelEndIndex  = mSelStartIndex;
    2238           0 :     mSelEndOffset = mSelStartOffset;
    2239             : 
    2240             :    //**** KDEBUG ****
    2241             :    // printf("\n* Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    2242             :    //**** KDEBUG ****
    2243             : 
    2244           0 :     return NS_OK;
    2245             :   }
    2246             : 
    2247             :   // Find the end of the selection in node offset terms:
    2248           0 :   int32_t endOffset = aOffset + aLength;
    2249           0 :   for (int32_t i = mOffsetTable.Length() - 1; !eNode && i >= 0; i--) {
    2250           0 :     entry = mOffsetTable[i];
    2251             : 
    2252           0 :     if (entry->mIsValid) {
    2253           0 :       if (entry->mIsInsertedText) {
    2254           0 :         if (entry->mStrOffset == eOffset) {
    2255             :           // If the selection ends on an inserted text offset entry,
    2256             :           // the selection includes the entire entry!
    2257             : 
    2258           0 :           eNode   = entry->mNode;
    2259           0 :           eOffset = entry->mNodeOffset + entry->mLength;
    2260             :         }
    2261           0 :       } else if (endOffset >= entry->mStrOffset &&
    2262           0 :                  endOffset <= entry->mStrOffset + entry->mLength) {
    2263           0 :         eNode   = entry->mNode;
    2264           0 :         eOffset = entry->mNodeOffset + endOffset - entry->mStrOffset;
    2265             :       }
    2266             : 
    2267           0 :       if (eNode) {
    2268           0 :         mSelEndIndex = i;
    2269           0 :         mSelEndOffset = endOffset;
    2270             :       }
    2271             :     }
    2272             :   }
    2273             : 
    2274           0 :   if (aDoUpdate && eNode) {
    2275           0 :     nsresult rv = selection->Extend(eNode, eOffset);
    2276             : 
    2277           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2278             :   }
    2279             : 
    2280             :   //**** KDEBUG ****
    2281             :   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
    2282             :   //**** KDEBUG ****
    2283             : 
    2284           0 :   return NS_OK;
    2285             : }
    2286             : 
    2287             : nsresult
    2288           0 : nsTextServicesDocument::GetSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
    2289             : {
    2290           0 :   NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
    2291             : 
    2292           0 :   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
    2293           0 :   *aSelOffset = -1;
    2294           0 :   *aSelLength = -1;
    2295             : 
    2296           0 :   NS_ENSURE_TRUE(mDOMDocument && mSelCon, NS_ERROR_FAILURE);
    2297             : 
    2298           0 :   if (mIteratorStatus == nsTextServicesDocument::eIsDone) {
    2299           0 :     return NS_OK;
    2300             :   }
    2301             : 
    2302           0 :   nsCOMPtr<nsISelection> selection;
    2303             :   bool isCollapsed;
    2304             : 
    2305           0 :   nsresult rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
    2306           0 :                                       getter_AddRefs(selection));
    2307             : 
    2308           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2309             : 
    2310           0 :   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
    2311             : 
    2312           0 :   rv = selection->GetIsCollapsed(&isCollapsed);
    2313             : 
    2314           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2315             : 
    2316             :   // XXX: If we expose this method publicly, we need to
    2317             :   //      add LOCK_DOC/UNLOCK_DOC calls!
    2318             : 
    2319             :   // LOCK_DOC(this);
    2320             : 
    2321           0 :   if (isCollapsed) {
    2322           0 :     rv = GetCollapsedSelection(aSelStatus, aSelOffset, aSelLength);
    2323             :   } else {
    2324           0 :     rv = GetUncollapsedSelection(aSelStatus, aSelOffset, aSelLength);
    2325             :   }
    2326             : 
    2327             :   // UNLOCK_DOC(this);
    2328             : 
    2329             :   // XXX The result of GetCollapsedSelection() or GetUncollapsedSelection().
    2330           0 :   return rv;
    2331             : }
    2332             : 
    2333             : nsresult
    2334           0 : nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
    2335             : {
    2336           0 :   nsCOMPtr<nsISelection> domSelection;
    2337             :   nsresult rv =
    2338           0 :     mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
    2339           0 :                           getter_AddRefs(domSelection));
    2340           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2341           0 :   NS_ENSURE_TRUE(domSelection, NS_ERROR_FAILURE);
    2342             : 
    2343           0 :   RefPtr<Selection> selection = domSelection->AsSelection();
    2344             : 
    2345             :   // The calling function should have done the GetIsCollapsed()
    2346             :   // check already. Just assume it's collapsed!
    2347           0 :   *aSelStatus = nsITextServicesDocument::eBlockOutside;
    2348           0 :   *aSelOffset = *aSelLength = -1;
    2349             : 
    2350           0 :   int32_t tableCount = mOffsetTable.Length();
    2351             : 
    2352           0 :   if (!tableCount) {
    2353           0 :     return NS_OK;
    2354             :   }
    2355             : 
    2356             :   // Get pointers to the first and last offset entries
    2357             :   // in the table.
    2358             : 
    2359           0 :   OffsetEntry* eStart = mOffsetTable[0];
    2360             :   OffsetEntry* eEnd;
    2361           0 :   if (tableCount > 1) {
    2362           0 :     eEnd = mOffsetTable[tableCount - 1];
    2363             :   } else {
    2364           0 :     eEnd = eStart;
    2365             :   }
    2366             : 
    2367           0 :   int32_t eStartOffset = eStart->mNodeOffset;
    2368           0 :   int32_t eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
    2369             : 
    2370           0 :   RefPtr<nsRange> range = selection->GetRangeAt(0);
    2371           0 :   NS_ENSURE_STATE(range);
    2372             : 
    2373           0 :   nsCOMPtr<nsIDOMNode> domParent;
    2374           0 :   rv = range->GetStartContainer(getter_AddRefs(domParent));
    2375           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2376             : 
    2377           0 :   nsCOMPtr<nsINode> parent = do_QueryInterface(domParent);
    2378           0 :   MOZ_ASSERT(parent);
    2379             : 
    2380             :   int32_t offset;
    2381           0 :   rv = range->GetStartOffset(&offset);
    2382           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2383             : 
    2384           0 :   int32_t e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
    2385           0 :                                                domParent, offset);
    2386           0 :   int32_t e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
    2387           0 :                                                domParent, offset);
    2388             : 
    2389           0 :   if (e1s1 > 0 || e2s1 < 0) {
    2390             :     // We're done if the caret is outside the current text block.
    2391           0 :     return NS_OK;
    2392             :   }
    2393             : 
    2394           0 :   if (parent->NodeType() == nsIDOMNode::TEXT_NODE) {
    2395             :     // Good news, the caret is in a text node. Look
    2396             :     // through the offset table for the entry that
    2397             :     // matches its parent and offset.
    2398             : 
    2399           0 :     for (int32_t i = 0; i < tableCount; i++) {
    2400           0 :       OffsetEntry* entry = mOffsetTable[i];
    2401           0 :       NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    2402             : 
    2403           0 :       if (entry->mNode == domParent.get() &&
    2404           0 :           entry->mNodeOffset <= offset &&
    2405           0 :           offset <= entry->mNodeOffset + entry->mLength) {
    2406           0 :         *aSelStatus = nsITextServicesDocument::eBlockContains;
    2407           0 :         *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
    2408           0 :         *aSelLength = 0;
    2409             : 
    2410           0 :         return NS_OK;
    2411             :       }
    2412             :     }
    2413             : 
    2414             :     // If we get here, we didn't find a text node entry
    2415             :     // in our offset table that matched.
    2416             : 
    2417           0 :     return NS_ERROR_FAILURE;
    2418             :   }
    2419             : 
    2420             :   // The caret is in our text block, but it's positioned in some
    2421             :   // non-text node (ex. <b>). Create a range based on the start
    2422             :   // and end of the text block, then create an iterator based on
    2423             :   // this range, with its initial position set to the closest
    2424             :   // child of this non-text node. Then look for the closest text
    2425             :   // node.
    2426             : 
    2427           0 :   rv = CreateRange(eStart->mNode, eStartOffset, eEnd->mNode, eEndOffset,
    2428           0 :                    getter_AddRefs(range));
    2429           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2430             : 
    2431           0 :   nsCOMPtr<nsIContentIterator> iter;
    2432           0 :   rv = CreateContentIterator(range, getter_AddRefs(iter));
    2433           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2434             : 
    2435             :   nsIContent* saveNode;
    2436           0 :   if (parent->HasChildren()) {
    2437             :     // XXX: We need to make sure that all of parent's
    2438             :     //      children are in the text block.
    2439             : 
    2440             :     // If the parent has children, position the iterator
    2441             :     // on the child that is to the left of the offset.
    2442             : 
    2443           0 :     uint32_t childIndex = (uint32_t)offset;
    2444             : 
    2445           0 :     if (childIndex > 0) {
    2446           0 :       uint32_t numChildren = parent->GetChildCount();
    2447           0 :       NS_ASSERTION(childIndex <= numChildren, "Invalid selection offset!");
    2448             : 
    2449           0 :       if (childIndex > numChildren) {
    2450           0 :         childIndex = numChildren;
    2451             :       }
    2452             : 
    2453           0 :       childIndex -= 1;
    2454             :     }
    2455             : 
    2456           0 :     nsIContent* content = parent->GetChildAt(childIndex);
    2457           0 :     NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
    2458             : 
    2459           0 :     rv = iter->PositionAt(content);
    2460           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2461             : 
    2462           0 :     saveNode = content;
    2463             :   } else {
    2464             :     // The parent has no children, so position the iterator
    2465             :     // on the parent.
    2466           0 :     NS_ENSURE_TRUE(parent->IsContent(), NS_ERROR_FAILURE);
    2467           0 :     nsCOMPtr<nsIContent> content = parent->AsContent();
    2468             : 
    2469           0 :     rv = iter->PositionAt(content);
    2470           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2471             : 
    2472           0 :     saveNode = content;
    2473             :   }
    2474             : 
    2475             :   // Now iterate to the left, towards the beginning of
    2476             :   // the text block, to find the first text node you
    2477             :   // come across.
    2478             : 
    2479           0 :   nsIContent* node = nullptr;
    2480           0 :   while (!iter->IsDone()) {
    2481           0 :     nsINode* current = iter->GetCurrentNode();
    2482           0 :     if (current->NodeType() == nsIDOMNode::TEXT_NODE) {
    2483           0 :       node = static_cast<nsIContent*>(current);
    2484           0 :       break;
    2485             :     }
    2486             : 
    2487           0 :     iter->Prev();
    2488             :   }
    2489             : 
    2490           0 :   if (node) {
    2491             :     // We found a node, now set the offset to the end
    2492             :     // of the text node.
    2493           0 :     offset = node->TextLength();
    2494             :   } else {
    2495             :     // We should never really get here, but I'm paranoid.
    2496             : 
    2497             :     // We didn't find a text node above, so iterate to
    2498             :     // the right, towards the end of the text block, looking
    2499             :     // for a text node.
    2500             : 
    2501           0 :     rv = iter->PositionAt(saveNode);
    2502           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2503             : 
    2504           0 :     node = nullptr;
    2505           0 :     while (!iter->IsDone()) {
    2506           0 :       nsINode* current = iter->GetCurrentNode();
    2507             : 
    2508           0 :       if (current->NodeType() == nsIDOMNode::TEXT_NODE) {
    2509           0 :         node = static_cast<nsIContent*>(current);
    2510           0 :         break;
    2511             :       }
    2512             : 
    2513           0 :       iter->Next();
    2514             :     }
    2515             : 
    2516           0 :     NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
    2517             : 
    2518             :     // We found a text node, so set the offset to
    2519             :     // the beginning of the node.
    2520             : 
    2521           0 :     offset = 0;
    2522             :   }
    2523             : 
    2524           0 :   for (int32_t i = 0; i < tableCount; i++) {
    2525           0 :     OffsetEntry* entry = mOffsetTable[i];
    2526           0 :     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    2527             : 
    2528           0 :     if (entry->mNode == node->AsDOMNode() &&
    2529           0 :         entry->mNodeOffset <= offset &&
    2530           0 :         offset <= entry->mNodeOffset + entry->mLength) {
    2531           0 :       *aSelStatus = nsITextServicesDocument::eBlockContains;
    2532           0 :       *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
    2533           0 :       *aSelLength = 0;
    2534             : 
    2535             :       // Now move the caret so that it is actually in the text node.
    2536             :       // We do this to keep things in sync.
    2537             :       //
    2538             :       // In most cases, the user shouldn't see any movement in the caret
    2539             :       // on screen.
    2540             : 
    2541           0 :       return SetSelectionInternal(*aSelOffset, *aSelLength, true);
    2542             :     }
    2543             :   }
    2544             : 
    2545           0 :   return NS_ERROR_FAILURE;
    2546             : }
    2547             : 
    2548             : nsresult
    2549           0 : nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
    2550             : {
    2551           0 :   RefPtr<nsRange> range;
    2552             :   OffsetEntry *entry;
    2553             : 
    2554           0 :   nsCOMPtr<nsISelection> domSelection;
    2555           0 :   nsresult rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
    2556           0 :                                       getter_AddRefs(domSelection));
    2557           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2558           0 :   NS_ENSURE_TRUE(domSelection, NS_ERROR_FAILURE);
    2559             : 
    2560           0 :   RefPtr<Selection> selection = domSelection->AsSelection();
    2561             : 
    2562             :   // It is assumed that the calling function has made sure that the
    2563             :   // selection is not collapsed, and that the input params to this
    2564             :   // method are initialized to some defaults.
    2565             : 
    2566           0 :   nsCOMPtr<nsIDOMNode> startContainer, endContainer;
    2567             :   int32_t startOffset, endOffset;
    2568             :   int32_t rangeCount, tableCount;
    2569           0 :   int32_t e1s1 = 0, e1s2 = 0, e2s1 = 0, e2s2 = 0;
    2570             : 
    2571             :   OffsetEntry *eStart, *eEnd;
    2572             :   int32_t eStartOffset, eEndOffset;
    2573             : 
    2574           0 :   tableCount = mOffsetTable.Length();
    2575             : 
    2576             :   // Get pointers to the first and last offset entries
    2577             :   // in the table.
    2578             : 
    2579           0 :   eStart = mOffsetTable[0];
    2580             : 
    2581           0 :   if (tableCount > 1) {
    2582           0 :     eEnd = mOffsetTable[tableCount - 1];
    2583             :   } else {
    2584           0 :     eEnd = eStart;
    2585             :   }
    2586             : 
    2587           0 :   eStartOffset = eStart->mNodeOffset;
    2588           0 :   eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
    2589             : 
    2590           0 :   rv = selection->GetRangeCount(&rangeCount);
    2591             : 
    2592           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2593             : 
    2594             :   // Find the first range in the selection that intersects
    2595             :   // the current text block.
    2596             : 
    2597           0 :   for (int32_t i = 0; i < rangeCount; i++) {
    2598           0 :     range = selection->GetRangeAt(i);
    2599           0 :     NS_ENSURE_STATE(range);
    2600             : 
    2601           0 :     rv = GetRangeEndPoints(range,
    2602           0 :                            getter_AddRefs(startContainer), &startOffset,
    2603           0 :                            getter_AddRefs(endContainer), &endOffset);
    2604             : 
    2605           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2606             : 
    2607           0 :     e1s2 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
    2608           0 :                                          endContainer, endOffset);
    2609           0 :     e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
    2610           0 :                                          startContainer, startOffset);
    2611             : 
    2612             :     // Break out of the loop if the text block intersects the current range.
    2613             : 
    2614           0 :     if (e1s2 <= 0 && e2s1 >= 0) {
    2615           0 :       break;
    2616             :     }
    2617             :   }
    2618             : 
    2619             :   // We're done if we didn't find an intersecting range.
    2620             : 
    2621           0 :   if (rangeCount < 1 || e1s2 > 0 || e2s1 < 0) {
    2622           0 :     *aSelStatus = nsITextServicesDocument::eBlockOutside;
    2623           0 :     *aSelOffset = *aSelLength = -1;
    2624           0 :     return NS_OK;
    2625             :   }
    2626             : 
    2627             :   // Now that we have an intersecting range, find out more info:
    2628             : 
    2629           0 :   e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
    2630           0 :                                        startContainer, startOffset);
    2631           0 :   e2s2 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
    2632           0 :                                        endContainer, endOffset);
    2633             : 
    2634           0 :   if (rangeCount > 1) {
    2635             :     // There are multiple selection ranges, we only deal
    2636             :     // with the first one that intersects the current,
    2637             :     // text block, so mark this a as a partial.
    2638           0 :     *aSelStatus = nsITextServicesDocument::eBlockPartial;
    2639           0 :   } else if (e1s1 > 0 && e2s2 < 0) {
    2640             :     // The range extends beyond the start and
    2641             :     // end of the current text block.
    2642           0 :     *aSelStatus = nsITextServicesDocument::eBlockInside;
    2643           0 :   } else if (e1s1 <= 0 && e2s2 >= 0) {
    2644             :     // The current text block contains the entire
    2645             :     // range.
    2646           0 :     *aSelStatus = nsITextServicesDocument::eBlockContains;
    2647             :   } else {
    2648             :     // The range partially intersects the block.
    2649           0 :     *aSelStatus = nsITextServicesDocument::eBlockPartial;
    2650             :   }
    2651             : 
    2652             :   // Now create a range based on the intersection of the
    2653             :   // text block and range:
    2654             : 
    2655           0 :   nsCOMPtr<nsIDOMNode> p1, p2;
    2656             :   int32_t     o1,  o2;
    2657             : 
    2658             :   // The start of the range will be the rightmost
    2659             :   // start node.
    2660             : 
    2661           0 :   if (e1s1 >= 0) {
    2662           0 :     p1 = do_QueryInterface(eStart->mNode);
    2663           0 :     o1 = eStartOffset;
    2664             :   } else {
    2665           0 :     p1 = startContainer;
    2666           0 :     o1 = startOffset;
    2667             :   }
    2668             : 
    2669             :   // The end of the range will be the leftmost
    2670             :   // end node.
    2671             : 
    2672           0 :   if (e2s2 <= 0) {
    2673           0 :     p2 = do_QueryInterface(eEnd->mNode);
    2674           0 :     o2 = eEndOffset;
    2675             :   } else {
    2676           0 :     p2 = endContainer;
    2677           0 :     o2 = endOffset;
    2678             :   }
    2679             : 
    2680           0 :   rv = CreateRange(p1, o1, p2, o2, getter_AddRefs(range));
    2681             : 
    2682           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2683             : 
    2684             :   // Now iterate over this range to figure out the selection's
    2685             :   // block offset and length.
    2686             : 
    2687           0 :   nsCOMPtr<nsIContentIterator> iter;
    2688             : 
    2689           0 :   rv = CreateContentIterator(range, getter_AddRefs(iter));
    2690             : 
    2691           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2692             : 
    2693             :   // Find the first text node in the range.
    2694             : 
    2695             :   bool found;
    2696           0 :   nsCOMPtr<nsIContent> content;
    2697             : 
    2698           0 :   iter->First();
    2699             : 
    2700           0 :   if (!IsTextNode(p1)) {
    2701           0 :     found = false;
    2702             : 
    2703           0 :     while (!iter->IsDone()) {
    2704           0 :       content = do_QueryInterface(iter->GetCurrentNode());
    2705             : 
    2706           0 :       if (IsTextNode(content)) {
    2707           0 :         p1 = do_QueryInterface(content);
    2708             : 
    2709           0 :         NS_ENSURE_TRUE(p1, NS_ERROR_FAILURE);
    2710             : 
    2711           0 :         o1 = 0;
    2712           0 :         found = true;
    2713             : 
    2714           0 :         break;
    2715             :       }
    2716             : 
    2717           0 :       iter->Next();
    2718             :     }
    2719             : 
    2720           0 :     NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
    2721             :   }
    2722             : 
    2723             :   // Find the last text node in the range.
    2724             : 
    2725           0 :   iter->Last();
    2726             : 
    2727           0 :   if (!IsTextNode(p2)) {
    2728           0 :     found = false;
    2729           0 :     while (!iter->IsDone()) {
    2730           0 :       content = do_QueryInterface(iter->GetCurrentNode());
    2731           0 :       if (IsTextNode(content)) {
    2732           0 :         p2 = do_QueryInterface(content);
    2733             : 
    2734           0 :         NS_ENSURE_TRUE(p2, NS_ERROR_FAILURE);
    2735             : 
    2736           0 :         nsString str;
    2737             : 
    2738           0 :         rv = p2->GetNodeValue(str);
    2739             : 
    2740           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2741             : 
    2742           0 :         o2 = str.Length();
    2743           0 :         found = true;
    2744             : 
    2745           0 :         break;
    2746             :       }
    2747             : 
    2748           0 :       iter->Prev();
    2749             :     }
    2750             : 
    2751           0 :     NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
    2752             :   }
    2753             : 
    2754           0 :   found    = false;
    2755           0 :   *aSelLength = 0;
    2756             : 
    2757           0 :   for (int32_t i = 0; i < tableCount; i++) {
    2758           0 :     entry = mOffsetTable[i];
    2759           0 :     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    2760           0 :     if (!found) {
    2761           0 :       if (entry->mNode == p1.get() &&
    2762           0 :           entry->mNodeOffset <= o1 &&
    2763           0 :           o1 <= entry->mNodeOffset + entry->mLength) {
    2764           0 :         *aSelOffset = entry->mStrOffset + (o1 - entry->mNodeOffset);
    2765           0 :         if (p1 == p2 &&
    2766           0 :             entry->mNodeOffset <= o2 &&
    2767           0 :             o2 <= entry->mNodeOffset + entry->mLength) {
    2768             :           // The start and end of the range are in the same offset
    2769             :           // entry. Calculate the length of the range then we're done.
    2770           0 :           *aSelLength = o2 - o1;
    2771           0 :           break;
    2772             :         }
    2773             :         // Add the length of the sub string in this offset entry
    2774             :         // that follows the start of the range.
    2775           0 :         *aSelLength = entry->mLength - (o1 - entry->mNodeOffset);
    2776           0 :         found = true;
    2777             :       }
    2778             :     } else { // Found.
    2779           0 :       if (entry->mNode == p2.get() &&
    2780           0 :           entry->mNodeOffset <= o2 &&
    2781           0 :           o2 <= entry->mNodeOffset + entry->mLength) {
    2782             :         // We found the end of the range. Calculate the length of the
    2783             :         // sub string that is before the end of the range, then we're done.
    2784           0 :         *aSelLength += o2 - entry->mNodeOffset;
    2785           0 :         break;
    2786             :       }
    2787             :       // The entire entry must be in the range.
    2788           0 :       *aSelLength += entry->mLength;
    2789             :     }
    2790             :   }
    2791             : 
    2792           0 :   return NS_OK;
    2793             : }
    2794             : 
    2795             : bool
    2796           0 : nsTextServicesDocument::SelectionIsCollapsed()
    2797             : {
    2798           0 :   return(mSelStartIndex == mSelEndIndex && mSelStartOffset == mSelEndOffset);
    2799             : }
    2800             : 
    2801             : bool
    2802           0 : nsTextServicesDocument::SelectionIsValid()
    2803             : {
    2804           0 :   return(mSelStartIndex >= 0);
    2805             : }
    2806             : 
    2807             : nsresult
    2808           0 : nsTextServicesDocument::GetRangeEndPoints(nsRange* aRange,
    2809             :                                           nsIDOMNode** aStartContainer,
    2810             :                                           int32_t* aStartOffset,
    2811             :                                           nsIDOMNode** aEndContainer,
    2812             :                                           int32_t* aEndOffset)
    2813             : {
    2814           0 :   NS_ENSURE_TRUE(aRange && aStartContainer && aStartOffset &&
    2815             :                  aEndContainer && aEndOffset, NS_ERROR_NULL_POINTER);
    2816             : 
    2817           0 :   nsresult rv = aRange->GetStartContainer(aStartContainer);
    2818             : 
    2819           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2820             : 
    2821           0 :   NS_ENSURE_TRUE(aStartContainer, NS_ERROR_FAILURE);
    2822             : 
    2823           0 :   rv = aRange->GetStartOffset(aStartOffset);
    2824             : 
    2825           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2826             : 
    2827           0 :   rv = aRange->GetEndContainer(aEndContainer);
    2828             : 
    2829           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2830             : 
    2831           0 :   NS_ENSURE_TRUE(aEndContainer, NS_ERROR_FAILURE);
    2832             : 
    2833           0 :   return aRange->GetEndOffset(aEndOffset);
    2834             : }
    2835             : 
    2836             : nsresult
    2837           0 : nsTextServicesDocument::CreateRange(nsIDOMNode* aStartContainer,
    2838             :                                     int32_t aStartOffset,
    2839             :                                     nsIDOMNode* aEndContainer,
    2840             :                                     int32_t aEndOffset,
    2841             :                                     nsRange** aRange)
    2842             : {
    2843             :   return nsRange::CreateRange(aStartContainer, aStartOffset, aEndContainer,
    2844           0 :                               aEndOffset, aRange);
    2845             : }
    2846             : 
    2847             : nsresult
    2848           0 : nsTextServicesDocument::FirstTextNode(nsIContentIterator *aIterator,
    2849             :                                       TSDIteratorStatus *aIteratorStatus)
    2850             : {
    2851           0 :   if (aIteratorStatus) {
    2852           0 :     *aIteratorStatus = nsTextServicesDocument::eIsDone;
    2853             :   }
    2854             : 
    2855           0 :   aIterator->First();
    2856             : 
    2857           0 :   while (!aIterator->IsDone()) {
    2858           0 :     if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
    2859           0 :       if (aIteratorStatus) {
    2860           0 :         *aIteratorStatus = nsTextServicesDocument::eValid;
    2861             :       }
    2862           0 :       break;
    2863             :     }
    2864           0 :     aIterator->Next();
    2865             :   }
    2866             : 
    2867           0 :   return NS_OK;
    2868             : }
    2869             : 
    2870             : nsresult
    2871           0 : nsTextServicesDocument::LastTextNode(nsIContentIterator *aIterator,
    2872             :                                      TSDIteratorStatus *aIteratorStatus)
    2873             : {
    2874           0 :   if (aIteratorStatus) {
    2875           0 :     *aIteratorStatus = nsTextServicesDocument::eIsDone;
    2876             :   }
    2877             : 
    2878           0 :   aIterator->Last();
    2879             : 
    2880           0 :   while (!aIterator->IsDone()) {
    2881           0 :     if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
    2882           0 :       if (aIteratorStatus) {
    2883           0 :         *aIteratorStatus = nsTextServicesDocument::eValid;
    2884             :       }
    2885           0 :       break;
    2886             :     }
    2887           0 :     aIterator->Prev();
    2888             :   }
    2889             : 
    2890           0 :   return NS_OK;
    2891             : }
    2892             : 
    2893             : nsresult
    2894           0 : nsTextServicesDocument::FirstTextNodeInCurrentBlock(nsIContentIterator *iter)
    2895             : {
    2896           0 :   NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
    2897             : 
    2898           0 :   ClearDidSkip(iter);
    2899             : 
    2900           0 :   nsCOMPtr<nsIContent> last;
    2901             : 
    2902             :   // Walk backwards over adjacent text nodes until
    2903             :   // we hit a block boundary:
    2904             : 
    2905           0 :   while (!iter->IsDone()) {
    2906           0 :     nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->IsContent()
    2907           0 :                                    ? iter->GetCurrentNode()->AsContent()
    2908           0 :                                    : nullptr;
    2909           0 :     if (last && IsBlockNode(content)) {
    2910           0 :       break;
    2911             :     }
    2912           0 :     if (IsTextNode(content)) {
    2913           0 :       if (last && !HasSameBlockNodeParent(content, last)) {
    2914             :         // We're done, the current text node is in a
    2915             :         // different block.
    2916           0 :         break;
    2917             :       }
    2918           0 :       last = content;
    2919             :     }
    2920             : 
    2921           0 :     iter->Prev();
    2922             : 
    2923           0 :     if (DidSkip(iter)) {
    2924           0 :       break;
    2925             :     }
    2926             :   }
    2927             : 
    2928           0 :   if (last) {
    2929           0 :     iter->PositionAt(last);
    2930             :   }
    2931             : 
    2932             :   // XXX: What should we return if last is null?
    2933             : 
    2934           0 :   return NS_OK;
    2935             : }
    2936             : 
    2937             : nsresult
    2938           0 : nsTextServicesDocument::FirstTextNodeInPrevBlock(nsIContentIterator *aIterator)
    2939             : {
    2940           0 :   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
    2941             : 
    2942             :   // XXX: What if mIterator is not currently on a text node?
    2943             : 
    2944             :   // Make sure mIterator is pointing to the first text node in the
    2945             :   // current block:
    2946             : 
    2947           0 :   nsresult rv = FirstTextNodeInCurrentBlock(aIterator);
    2948             : 
    2949           0 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
    2950             : 
    2951             :   // Point mIterator to the first node before the first text node:
    2952             : 
    2953           0 :   aIterator->Prev();
    2954             : 
    2955           0 :   if (aIterator->IsDone()) {
    2956           0 :     return NS_ERROR_FAILURE;
    2957             :   }
    2958             : 
    2959             :   // Now find the first text node of the next block:
    2960             : 
    2961           0 :   return FirstTextNodeInCurrentBlock(aIterator);
    2962             : }
    2963             : 
    2964             : nsresult
    2965           0 : nsTextServicesDocument::FirstTextNodeInNextBlock(nsIContentIterator *aIterator)
    2966             : {
    2967           0 :   nsCOMPtr<nsIContent> prev;
    2968           0 :   bool crossedBlockBoundary = false;
    2969             : 
    2970           0 :   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
    2971             : 
    2972           0 :   ClearDidSkip(aIterator);
    2973             : 
    2974           0 :   while (!aIterator->IsDone()) {
    2975           0 :     nsCOMPtr<nsIContent> content = aIterator->GetCurrentNode()->IsContent()
    2976           0 :                                    ? aIterator->GetCurrentNode()->AsContent()
    2977           0 :                                    : nullptr;
    2978             : 
    2979           0 :     if (IsTextNode(content)) {
    2980           0 :       if (crossedBlockBoundary ||
    2981           0 :           (prev && !HasSameBlockNodeParent(prev, content))) {
    2982           0 :         break;
    2983             :       }
    2984           0 :       prev = content;
    2985           0 :     } else if (!crossedBlockBoundary && IsBlockNode(content)) {
    2986           0 :       crossedBlockBoundary = true;
    2987             :     }
    2988             : 
    2989           0 :     aIterator->Next();
    2990             : 
    2991           0 :     if (!crossedBlockBoundary && DidSkip(aIterator)) {
    2992           0 :       crossedBlockBoundary = true;
    2993             :     }
    2994             :   }
    2995             : 
    2996           0 :   return NS_OK;
    2997             : }
    2998             : 
    2999             : nsresult
    3000           0 : nsTextServicesDocument::GetFirstTextNodeInPrevBlock(nsIContent **aContent)
    3001             : {
    3002           0 :   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
    3003             : 
    3004           0 :   *aContent = 0;
    3005             : 
    3006             :   // Save the iterator's current content node so we can restore
    3007             :   // it when we are done:
    3008             : 
    3009           0 :   nsINode* node = mIterator->GetCurrentNode();
    3010             : 
    3011           0 :   nsresult rv = FirstTextNodeInPrevBlock(mIterator);
    3012             : 
    3013           0 :   if (NS_FAILED(rv)) {
    3014             :     // Try to restore the iterator before returning.
    3015           0 :     mIterator->PositionAt(node);
    3016           0 :     return rv;
    3017             :   }
    3018             : 
    3019           0 :   if (!mIterator->IsDone()) {
    3020           0 :     nsCOMPtr<nsIContent> current = mIterator->GetCurrentNode()->IsContent()
    3021           0 :                                    ? mIterator->GetCurrentNode()->AsContent()
    3022           0 :                                    : nullptr;
    3023           0 :     current.forget(aContent);
    3024             :   }
    3025             : 
    3026             :   // Restore the iterator:
    3027             : 
    3028           0 :   return mIterator->PositionAt(node);
    3029             : }
    3030             : 
    3031             : nsresult
    3032           0 : nsTextServicesDocument::GetFirstTextNodeInNextBlock(nsIContent **aContent)
    3033             : {
    3034           0 :   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
    3035             : 
    3036           0 :   *aContent = 0;
    3037             : 
    3038             :   // Save the iterator's current content node so we can restore
    3039             :   // it when we are done:
    3040             : 
    3041           0 :   nsINode* node = mIterator->GetCurrentNode();
    3042             : 
    3043           0 :   nsresult rv = FirstTextNodeInNextBlock(mIterator);
    3044             : 
    3045           0 :   if (NS_FAILED(rv)) {
    3046             :     // Try to restore the iterator before returning.
    3047           0 :     mIterator->PositionAt(node);
    3048           0 :     return rv;
    3049             :   }
    3050             : 
    3051           0 :   if (!mIterator->IsDone()) {
    3052           0 :     nsCOMPtr<nsIContent> current = mIterator->GetCurrentNode()->IsContent()
    3053           0 :                                    ? mIterator->GetCurrentNode()->AsContent()
    3054           0 :                                    : nullptr;
    3055           0 :     current.forget(aContent);
    3056             :   }
    3057             : 
    3058             :   // Restore the iterator:
    3059           0 :   return mIterator->PositionAt(node);
    3060             : }
    3061             : 
    3062             : nsresult
    3063           0 : nsTextServicesDocument::CreateOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable,
    3064             :                                           nsIContentIterator *aIterator,
    3065             :                                           TSDIteratorStatus *aIteratorStatus,
    3066             :                                           nsRange* aIterRange, nsString* aStr)
    3067             : {
    3068           0 :   nsCOMPtr<nsIContent> first;
    3069           0 :   nsCOMPtr<nsIContent> prev;
    3070             : 
    3071           0 :   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
    3072             : 
    3073           0 :   ClearOffsetTable(aOffsetTable);
    3074             : 
    3075           0 :   if (aStr) {
    3076           0 :     aStr->Truncate();
    3077             :   }
    3078             : 
    3079           0 :   if (*aIteratorStatus == nsTextServicesDocument::eIsDone) {
    3080           0 :     return NS_OK;
    3081             :   }
    3082             : 
    3083             :   // If we have an aIterRange, retrieve the endpoints so
    3084             :   // they can be used in the while loop below to trim entries
    3085             :   // for text nodes that are partially selected by aIterRange.
    3086             : 
    3087           0 :   nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
    3088           0 :   int32_t rngStartOffset = 0, rngEndOffset = 0;
    3089             : 
    3090           0 :   if (aIterRange) {
    3091             :     nsresult rv =
    3092           0 :       GetRangeEndPoints(aIterRange,
    3093           0 :                         getter_AddRefs(rngStartNode), &rngStartOffset,
    3094           0 :                         getter_AddRefs(rngEndNode), &rngEndOffset);
    3095             : 
    3096           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3097             :   }
    3098             : 
    3099             :   // The text service could have added text nodes to the beginning
    3100             :   // of the current block and called this method again. Make sure
    3101             :   // we really are at the beginning of the current block:
    3102             : 
    3103           0 :   nsresult rv = FirstTextNodeInCurrentBlock(aIterator);
    3104             : 
    3105           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3106             : 
    3107           0 :   int32_t offset = 0;
    3108             : 
    3109           0 :   ClearDidSkip(aIterator);
    3110             : 
    3111           0 :   while (!aIterator->IsDone()) {
    3112           0 :     nsCOMPtr<nsIContent> content = aIterator->GetCurrentNode()->IsContent()
    3113           0 :                                    ? aIterator->GetCurrentNode()->AsContent()
    3114           0 :                                    : nullptr;
    3115           0 :     if (IsTextNode(content)) {
    3116           0 :       if (prev && !HasSameBlockNodeParent(prev, content)) {
    3117           0 :         break;
    3118             :       }
    3119             : 
    3120           0 :       nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
    3121           0 :       if (node) {
    3122           0 :         nsString str;
    3123             : 
    3124           0 :         rv = node->GetNodeValue(str);
    3125             : 
    3126           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3127             : 
    3128             :         // Add an entry for this text node into the offset table:
    3129             : 
    3130           0 :         OffsetEntry *entry = new OffsetEntry(node, offset, str.Length());
    3131           0 :         aOffsetTable->AppendElement(entry);
    3132             : 
    3133             :         // If one or both of the endpoints of the iteration range
    3134             :         // are in the text node for this entry, make sure the entry
    3135             :         // only accounts for the portion of the text node that is
    3136             :         // in the range.
    3137             : 
    3138           0 :         int32_t startOffset = 0;
    3139           0 :         int32_t endOffset   = str.Length();
    3140           0 :         bool adjustStr    = false;
    3141             : 
    3142           0 :         if (entry->mNode == rngStartNode) {
    3143           0 :           entry->mNodeOffset = startOffset = rngStartOffset;
    3144           0 :           adjustStr = true;
    3145             :         }
    3146             : 
    3147           0 :         if (entry->mNode == rngEndNode) {
    3148           0 :           endOffset = rngEndOffset;
    3149           0 :           adjustStr = true;
    3150             :         }
    3151             : 
    3152           0 :         if (adjustStr) {
    3153           0 :           entry->mLength = endOffset - startOffset;
    3154           0 :           str = Substring(str, startOffset, entry->mLength);
    3155             :         }
    3156             : 
    3157           0 :         offset += str.Length();
    3158             : 
    3159           0 :         if (aStr) {
    3160             :           // Append the text node's string to the output string:
    3161           0 :           if (!first) {
    3162           0 :             *aStr = str;
    3163             :           } else {
    3164           0 :             *aStr += str;
    3165             :           }
    3166             :         }
    3167             :       }
    3168             : 
    3169           0 :       prev = content;
    3170             : 
    3171           0 :       if (!first) {
    3172           0 :         first = content;
    3173             :       }
    3174             :     }
    3175             :     // XXX This should be checked before IsTextNode(), but IsBlockNode() returns
    3176             :     //     true even if content is a text node.  See bug 1311934.
    3177           0 :     else if (IsBlockNode(content)) {
    3178           0 :       break;
    3179             :     }
    3180             : 
    3181           0 :     aIterator->Next();
    3182             : 
    3183           0 :     if (DidSkip(aIterator)) {
    3184           0 :       break;
    3185             :     }
    3186             :   }
    3187             : 
    3188           0 :   if (first) {
    3189             :     // Always leave the iterator pointing at the first
    3190             :     // text node of the current block!
    3191           0 :     aIterator->PositionAt(first);
    3192             :   } else {
    3193             :     // If we never ran across a text node, the iterator
    3194             :     // might have been pointing to something invalid to
    3195             :     // begin with.
    3196           0 :     *aIteratorStatus = nsTextServicesDocument::eIsDone;
    3197             :   }
    3198             : 
    3199           0 :   return NS_OK;
    3200             : }
    3201             : 
    3202             : nsresult
    3203           0 : nsTextServicesDocument::RemoveInvalidOffsetEntries()
    3204             : {
    3205           0 :   for (size_t i = 0; i < mOffsetTable.Length(); ) {
    3206           0 :     OffsetEntry* entry = mOffsetTable[i];
    3207           0 :     if (!entry->mIsValid) {
    3208           0 :       mOffsetTable.RemoveElementAt(i);
    3209           0 :       if (mSelStartIndex >= 0 && static_cast<size_t>(mSelStartIndex) >= i) {
    3210             :         // We are deleting an entry that comes before
    3211             :         // mSelStartIndex, decrement mSelStartIndex so
    3212             :         // that it points to the correct entry!
    3213             : 
    3214           0 :         NS_ASSERTION(i != static_cast<size_t>(mSelStartIndex),
    3215             :                      "Invalid selection index.");
    3216             : 
    3217           0 :         --mSelStartIndex;
    3218           0 :         --mSelEndIndex;
    3219             :       }
    3220             :     } else {
    3221           0 :       i++;
    3222             :     }
    3223             :   }
    3224             : 
    3225           0 :   return NS_OK;
    3226             : }
    3227             : 
    3228             : nsresult
    3229           0 : nsTextServicesDocument::ClearOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable)
    3230             : {
    3231           0 :   for (size_t i = 0; i < aOffsetTable->Length(); i++) {
    3232           0 :     delete aOffsetTable->ElementAt(i);
    3233             :   }
    3234             : 
    3235           0 :   aOffsetTable->Clear();
    3236             : 
    3237           0 :   return NS_OK;
    3238             : }
    3239             : 
    3240             : nsresult
    3241           0 : nsTextServicesDocument::SplitOffsetEntry(int32_t aTableIndex, int32_t aNewEntryLength)
    3242             : {
    3243           0 :   OffsetEntry *entry = mOffsetTable[aTableIndex];
    3244             : 
    3245           0 :   NS_ASSERTION((aNewEntryLength > 0), "aNewEntryLength <= 0");
    3246           0 :   NS_ASSERTION((aNewEntryLength < entry->mLength), "aNewEntryLength >= mLength");
    3247             : 
    3248           0 :   if (aNewEntryLength < 1 || aNewEntryLength >= entry->mLength) {
    3249           0 :     return NS_ERROR_FAILURE;
    3250             :   }
    3251             : 
    3252           0 :   int32_t oldLength = entry->mLength - aNewEntryLength;
    3253             : 
    3254             :   OffsetEntry *newEntry = new OffsetEntry(entry->mNode,
    3255           0 :                                           entry->mStrOffset + oldLength,
    3256           0 :                                           aNewEntryLength);
    3257             : 
    3258           0 :   if (!mOffsetTable.InsertElementAt(aTableIndex + 1, newEntry)) {
    3259           0 :     delete newEntry;
    3260           0 :     return NS_ERROR_FAILURE;
    3261             :   }
    3262             : 
    3263             :    // Adjust entry fields:
    3264             : 
    3265           0 :    entry->mLength        = oldLength;
    3266           0 :    newEntry->mNodeOffset = entry->mNodeOffset + oldLength;
    3267             : 
    3268           0 :   return NS_OK;
    3269             : }
    3270             : 
    3271             : nsresult
    3272           0 : nsTextServicesDocument::NodeHasOffsetEntry(nsTArray<OffsetEntry*> *aOffsetTable, nsIDOMNode *aNode, bool *aHasEntry, int32_t *aEntryIndex)
    3273             : {
    3274           0 :   NS_ENSURE_TRUE(aNode && aHasEntry && aEntryIndex, NS_ERROR_NULL_POINTER);
    3275             : 
    3276           0 :   for (size_t i = 0; i < aOffsetTable->Length(); i++) {
    3277           0 :     OffsetEntry* entry = (*aOffsetTable)[i];
    3278             : 
    3279           0 :     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
    3280             : 
    3281           0 :     if (entry->mNode == aNode) {
    3282           0 :       *aHasEntry   = true;
    3283           0 :       *aEntryIndex = i;
    3284           0 :       return NS_OK;
    3285             :     }
    3286             :   }
    3287             : 
    3288           0 :   *aHasEntry   = false;
    3289           0 :   *aEntryIndex = -1;
    3290           0 :   return NS_OK;
    3291             : }
    3292             : 
    3293             : // Spellchecker code has this. See bug 211343
    3294             : #define IS_NBSP_CHAR(c) (((unsigned char)0xa0)==(c))
    3295             : 
    3296             : nsresult
    3297           0 : nsTextServicesDocument::FindWordBounds(nsTArray<OffsetEntry*> *aOffsetTable,
    3298             :                                        nsString *aBlockStr,
    3299             :                                        nsIDOMNode *aNode,
    3300             :                                        int32_t aNodeOffset,
    3301             :                                        nsIDOMNode **aWordStartNode,
    3302             :                                        int32_t *aWordStartOffset,
    3303             :                                        nsIDOMNode **aWordEndNode,
    3304             :                                        int32_t *aWordEndOffset)
    3305             : {
    3306             :   // Initialize return values.
    3307             : 
    3308           0 :   if (aWordStartNode) {
    3309           0 :     *aWordStartNode = nullptr;
    3310             :   }
    3311           0 :   if (aWordStartOffset) {
    3312           0 :     *aWordStartOffset = 0;
    3313             :   }
    3314           0 :   if (aWordEndNode) {
    3315           0 :     *aWordEndNode = nullptr;
    3316             :   }
    3317           0 :   if (aWordEndOffset) {
    3318           0 :     *aWordEndOffset = 0;
    3319             :   }
    3320             : 
    3321           0 :   int32_t entryIndex = 0;
    3322           0 :   bool hasEntry = false;
    3323             : 
    3324             :   // It's assumed that aNode is a text node. The first thing
    3325             :   // we do is get its index in the offset table so we can
    3326             :   // calculate the dom point's string offset.
    3327             : 
    3328           0 :   nsresult rv = NodeHasOffsetEntry(aOffsetTable, aNode, &hasEntry, &entryIndex);
    3329           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3330           0 :   NS_ENSURE_TRUE(hasEntry, NS_ERROR_FAILURE);
    3331             : 
    3332             :   // Next we map aNodeOffset into a string offset.
    3333             : 
    3334           0 :   OffsetEntry *entry = (*aOffsetTable)[entryIndex];
    3335           0 :   uint32_t strOffset = entry->mStrOffset + aNodeOffset - entry->mNodeOffset;
    3336             : 
    3337             :   // Now we use the word breaker to find the beginning and end
    3338             :   // of the word from our calculated string offset.
    3339             : 
    3340           0 :   const char16_t *str = aBlockStr->get();
    3341           0 :   uint32_t strLen = aBlockStr->Length();
    3342             : 
    3343           0 :   nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
    3344           0 :   nsWordRange res = wordBreaker->FindWord(str, strLen, strOffset);
    3345           0 :   if (res.mBegin > strLen) {
    3346           0 :     return str ? NS_ERROR_ILLEGAL_VALUE : NS_ERROR_NULL_POINTER;
    3347             :   }
    3348             : 
    3349             :   // Strip out the NBSPs at the ends
    3350           0 :   while (res.mBegin <= res.mEnd && IS_NBSP_CHAR(str[res.mBegin])) {
    3351           0 :     res.mBegin++;
    3352             :   }
    3353           0 :   if (str[res.mEnd] == (unsigned char)0x20) {
    3354           0 :     uint32_t realEndWord = res.mEnd - 1;
    3355           0 :     while (realEndWord > res.mBegin && IS_NBSP_CHAR(str[realEndWord])) {
    3356           0 :       realEndWord--;
    3357             :     }
    3358           0 :     if (realEndWord < res.mEnd - 1) {
    3359           0 :       res.mEnd = realEndWord + 1;
    3360             :     }
    3361             :   }
    3362             : 
    3363             :   // Now that we have the string offsets for the beginning
    3364             :   // and end of the word, run through the offset table and
    3365             :   // convert them back into dom points.
    3366             : 
    3367           0 :   size_t lastIndex = aOffsetTable->Length() - 1;
    3368           0 :   for (size_t i = 0; i <= lastIndex; i++) {
    3369           0 :     entry = (*aOffsetTable)[i];
    3370             : 
    3371           0 :     int32_t strEndOffset = entry->mStrOffset + entry->mLength;
    3372             : 
    3373             :     // Check to see if res.mBegin is within the range covered
    3374             :     // by this entry. Note that if res.mBegin is after the last
    3375             :     // character covered by this entry, we will use the next
    3376             :     // entry if there is one.
    3377             : 
    3378           0 :     if (uint32_t(entry->mStrOffset) <= res.mBegin &&
    3379           0 :         (res.mBegin < static_cast<uint32_t>(strEndOffset) ||
    3380           0 :          (res.mBegin == static_cast<uint32_t>(strEndOffset) &&
    3381             :           i == lastIndex))) {
    3382           0 :       if (aWordStartNode) {
    3383           0 :         *aWordStartNode = entry->mNode;
    3384           0 :         NS_IF_ADDREF(*aWordStartNode);
    3385             :       }
    3386             : 
    3387           0 :       if (aWordStartOffset) {
    3388           0 :         *aWordStartOffset = entry->mNodeOffset + res.mBegin - entry->mStrOffset;
    3389             :       }
    3390             : 
    3391           0 :       if (!aWordEndNode && !aWordEndOffset) {
    3392             :         // We've found our start entry, but if we're not looking
    3393             :         // for end entries, we're done.
    3394           0 :         break;
    3395             :       }
    3396             :     }
    3397             : 
    3398             :     // Check to see if res.mEnd is within the range covered
    3399             :     // by this entry.
    3400             : 
    3401           0 :     if (static_cast<uint32_t>(entry->mStrOffset) <= res.mEnd &&
    3402           0 :         res.mEnd <= static_cast<uint32_t>(strEndOffset)) {
    3403           0 :       if (res.mBegin == res.mEnd &&
    3404           0 :           res.mEnd == static_cast<uint32_t>(strEndOffset) &&
    3405             :           i != lastIndex) {
    3406             :         // Wait for the next round so that we use the same entry
    3407             :         // we did for aWordStartNode.
    3408           0 :         continue;
    3409             :       }
    3410             : 
    3411           0 :       if (aWordEndNode) {
    3412           0 :         *aWordEndNode = entry->mNode;
    3413           0 :         NS_IF_ADDREF(*aWordEndNode);
    3414             :       }
    3415             : 
    3416           0 :       if (aWordEndOffset) {
    3417           0 :         *aWordEndOffset = entry->mNodeOffset + res.mEnd - entry->mStrOffset;
    3418             :       }
    3419           0 :       break;
    3420             :     }
    3421             :   }
    3422             : 
    3423             : 
    3424           0 :   return NS_OK;
    3425             : }
    3426             : 
    3427             : NS_IMETHODIMP
    3428           0 : nsTextServicesDocument::WillInsertNode(nsIDOMNode *aNode,
    3429             :                               nsIDOMNode *aParent,
    3430             :                               int32_t     aPosition)
    3431             : {
    3432           0 :   return NS_OK;
    3433             : }
    3434             : 
    3435             : NS_IMETHODIMP
    3436           0 : nsTextServicesDocument::WillDeleteNode(nsIDOMNode *aChild)
    3437             : {
    3438           0 :   return NS_OK;
    3439             : }
    3440             : 
    3441             : NS_IMETHODIMP
    3442           0 : nsTextServicesDocument::WillSplitNode(nsIDOMNode *aExistingRightNode,
    3443             :                              int32_t     aOffset)
    3444             : {
    3445           0 :   return NS_OK;
    3446             : }
    3447             : 
    3448             : NS_IMETHODIMP
    3449           0 : nsTextServicesDocument::WillJoinNodes(nsIDOMNode  *aLeftNode,
    3450             :                              nsIDOMNode  *aRightNode,
    3451             :                              nsIDOMNode  *aParent)
    3452             : {
    3453           0 :   return NS_OK;
    3454             : }
    3455             : 
    3456             : // -------------------------------
    3457             : // stubs for unused listen methods
    3458             : // -------------------------------
    3459             : 
    3460             : NS_IMETHODIMP
    3461           0 : nsTextServicesDocument::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition)
    3462             : {
    3463           0 :   return NS_OK;
    3464             : }
    3465             : 
    3466             : NS_IMETHODIMP
    3467           0 : nsTextServicesDocument::DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition, nsresult aResult)
    3468             : {
    3469           0 :   return NS_OK;
    3470             : }
    3471             : 
    3472             : NS_IMETHODIMP
    3473           0 : nsTextServicesDocument::WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString)
    3474             : {
    3475           0 :   return NS_OK;
    3476             : }
    3477             : 
    3478             : NS_IMETHODIMP
    3479           0 : nsTextServicesDocument::DidInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString, nsresult aResult)
    3480             : {
    3481           0 :   return NS_OK;
    3482             : }
    3483             : 
    3484             : NS_IMETHODIMP
    3485           0 : nsTextServicesDocument::WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength)
    3486             : {
    3487           0 :   return NS_OK;
    3488             : }
    3489             : 
    3490             : NS_IMETHODIMP
    3491           0 : nsTextServicesDocument::DidDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength, nsresult aResult)
    3492             : {
    3493           0 :   return NS_OK;
    3494             : }
    3495             : 
    3496             : NS_IMETHODIMP
    3497           0 : nsTextServicesDocument::WillDeleteSelection(nsISelection *aSelection)
    3498             : {
    3499           0 :   return NS_OK;
    3500             : }
    3501             : 
    3502             : NS_IMETHODIMP
    3503           0 : nsTextServicesDocument::DidDeleteSelection(nsISelection *aSelection)
    3504             : {
    3505           0 :   return NS_OK;
    3506             : }

Generated by: LCOV version 1.13