editor/txtsvc/src/nsTextServicesDocument.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     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/. */
     6 #include <stddef.h>                     // for nullptr
     8 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
     9 #include "mozilla/mozalloc.h"           // for operator new, etc
    10 #include "nsAString.h"                  // for nsAString_internal::Length, etc
    11 #include "nsAutoPtr.h"                  // for nsRefPtr
    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
    41 #define LOCK_DOC(doc)
    42 #define UNLOCK_DOC(doc)
    44 using namespace mozilla;
    46 class OffsetEntry
    47 {
    48 public:
    49   OffsetEntry(nsIDOMNode *aNode, int32_t aOffset, int32_t aLength)
    50     : mNode(aNode), mNodeOffset(0), mStrOffset(aOffset), mLength(aLength),
    51       mIsInsertedText(false), mIsValid(true)
    52   {
    53     if (mStrOffset < 1)
    54       mStrOffset = 0;
    56     if (mLength < 1)
    57       mLength = 0;
    58   }
    60   virtual ~OffsetEntry()
    61   {
    62     mNode       = 0;
    63     mNodeOffset = 0;
    64     mStrOffset  = 0;
    65     mLength     = 0;
    66     mIsValid    = false;
    67   }
    69   nsIDOMNode *mNode;
    70   int32_t mNodeOffset;
    71   int32_t mStrOffset;
    72   int32_t mLength;
    73   bool    mIsInsertedText;
    74   bool    mIsValid;
    75 };
    77 #define TS_ATOM(name_, value_) nsIAtom* nsTextServicesDocument::name_ = 0;
    78 #include "nsTSAtomList.h" // IWYU pragma: keep
    79 #undef TS_ATOM
    81 nsTextServicesDocument::nsTextServicesDocument()
    82 {
    83   mRefCnt         = 0;
    85   mSelStartIndex  = -1;
    86   mSelStartOffset = -1;
    87   mSelEndIndex    = -1;
    88   mSelEndOffset   = -1;
    90   mIteratorStatus = eIsDone;
    91 }
    93 nsTextServicesDocument::~nsTextServicesDocument()
    94 {
    95   ClearOffsetTable(&mOffsetTable);
    96 }
    98 #define TS_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
    99 #include "nsTSAtomList.h" // IWYU pragma: keep
   100 #undef TS_ATOM
   102 /* static */
   103 void
   104 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   };
   112   NS_RegisterStaticAtoms(ts_atoms);
   113 }
   115 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextServicesDocument)
   116 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextServicesDocument)
   118 NS_INTERFACE_MAP_BEGIN(nsTextServicesDocument)
   119   NS_INTERFACE_MAP_ENTRY(nsITextServicesDocument)
   120   NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
   121   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITextServicesDocument)
   122   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsTextServicesDocument)
   123 NS_INTERFACE_MAP_END
   125 NS_IMPL_CYCLE_COLLECTION(nsTextServicesDocument,
   126                          mDOMDocument,
   127                          mSelCon,
   128                          mIterator,
   129                          mPrevTextBlock,
   130                          mNextTextBlock,
   131                          mExtent,
   132                          mTxtSvcFilter)
   134 NS_IMETHODIMP
   135 nsTextServicesDocument::InitWithEditor(nsIEditor *aEditor)
   136 {
   137   nsresult result = NS_OK;
   138   nsCOMPtr<nsISelectionController> selCon;
   139   nsCOMPtr<nsIDOMDocument> doc;
   141   NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
   143   LOCK_DOC(this);
   145   // Check to see if we already have an mSelCon. If we do, it
   146   // better be the same one the editor uses!
   148   result = aEditor->GetSelectionController(getter_AddRefs(selCon));
   150   if (NS_FAILED(result))
   151   {
   152     UNLOCK_DOC(this);
   153     return result;
   154   }
   156   if (!selCon || (mSelCon && selCon != mSelCon))
   157   {
   158     UNLOCK_DOC(this);
   159     return NS_ERROR_FAILURE;
   160   }
   162   if (!mSelCon)
   163     mSelCon = selCon;
   165   // Check to see if we already have an mDOMDocument. If we do, it
   166   // better be the same one the editor uses!
   168   result = aEditor->GetDocument(getter_AddRefs(doc));
   170   if (NS_FAILED(result))
   171   {
   172     UNLOCK_DOC(this);
   173     return result;
   174   }
   176   if (!doc || (mDOMDocument && doc != mDOMDocument))
   177   {
   178     UNLOCK_DOC(this);
   179     return NS_ERROR_FAILURE;
   180   }
   182   if (!mDOMDocument)
   183   {
   184     mDOMDocument = doc;
   186     result = CreateDocumentContentIterator(getter_AddRefs(mIterator));
   188     if (NS_FAILED(result))
   189     {
   190       UNLOCK_DOC(this);
   191       return result;
   192     }
   194     mIteratorStatus = nsTextServicesDocument::eIsDone;
   196     result = FirstBlock();
   198     if (NS_FAILED(result))
   199     {
   200       UNLOCK_DOC(this);
   201       return result;
   202     }
   203   }
   205   mEditor = do_GetWeakReference(aEditor);
   207   result = aEditor->AddEditActionListener(this);
   209   UNLOCK_DOC(this);
   211   return result;
   212 }
   214 NS_IMETHODIMP 
   215 nsTextServicesDocument::GetDocument(nsIDOMDocument **aDoc)
   216 {
   217   NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
   219   *aDoc = nullptr; // init out param
   220   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_NOT_INITIALIZED);
   222   *aDoc = mDOMDocument;
   223   NS_ADDREF(*aDoc);
   225   return NS_OK;
   226 }
   228 NS_IMETHODIMP
   229 nsTextServicesDocument::SetExtent(nsIDOMRange* aDOMRange)
   230 {
   231   NS_ENSURE_ARG_POINTER(aDOMRange);
   232   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
   234   LOCK_DOC(this);
   236   // We need to store a copy of aDOMRange since we don't
   237   // know where it came from.
   239   nsresult result = aDOMRange->CloneRange(getter_AddRefs(mExtent));
   241   if (NS_FAILED(result))
   242   {
   243     UNLOCK_DOC(this);
   244     return result;
   245   }
   247   // Create a new iterator based on our new extent range.
   249   result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
   251   if (NS_FAILED(result))
   252   {
   253     UNLOCK_DOC(this);
   254     return result;
   255   }
   257   // Now position the iterator at the start of the first block
   258   // in the range.
   260   mIteratorStatus = nsTextServicesDocument::eIsDone;
   262   result = FirstBlock();
   264   UNLOCK_DOC(this);
   266   return result;
   267 }
   269 NS_IMETHODIMP
   270 nsTextServicesDocument::ExpandRangeToWordBoundaries(nsIDOMRange *aRange)
   271 {
   272   NS_ENSURE_ARG_POINTER(aRange);
   274   // Get the end points of the range.
   276   nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
   277   int32_t rngStartOffset, rngEndOffset;
   279   nsresult result =  GetRangeEndPoints(aRange,
   280                                        getter_AddRefs(rngStartNode),
   281                                        &rngStartOffset,
   282                                        getter_AddRefs(rngEndNode),
   283                                        &rngEndOffset);
   285   NS_ENSURE_SUCCESS(result, result);
   287   // Create a content iterator based on the range.
   289   nsCOMPtr<nsIContentIterator> iter;
   290   result = CreateContentIterator(aRange, getter_AddRefs(iter));
   292   NS_ENSURE_SUCCESS(result, result);
   294   // Find the first text node in the range.
   296   TSDIteratorStatus iterStatus;
   298   result = FirstTextNode(iter, &iterStatus);
   299   NS_ENSURE_SUCCESS(result, result);
   301   if (iterStatus == nsTextServicesDocument::eIsDone)
   302   {
   303     // No text was found so there's no adjustment necessary!
   304     return NS_OK;
   305   }
   307   nsINode *firstText = iter->GetCurrentNode();
   308   NS_ENSURE_TRUE(firstText, NS_ERROR_FAILURE);
   310   // Find the last text node in the range.
   312   result = LastTextNode(iter, &iterStatus);
   313   NS_ENSURE_SUCCESS(result, result);
   315   if (iterStatus == nsTextServicesDocument::eIsDone)
   316   {
   317     // We should never get here because a first text block
   318     // was found above.
   319     NS_ASSERTION(false, "Found a first without a last!");
   320     return NS_ERROR_FAILURE;
   321   }
   323   nsINode *lastText = iter->GetCurrentNode();
   324   NS_ENSURE_TRUE(lastText, NS_ERROR_FAILURE);
   326   // Now make sure our end points are in terms of text nodes in the range!
   328   nsCOMPtr<nsIDOMNode> firstTextNode = do_QueryInterface(firstText);
   329   NS_ENSURE_TRUE(firstTextNode, NS_ERROR_FAILURE);
   331   if (rngStartNode != firstTextNode)
   332   {
   333     // The range includes the start of the first text node!
   334     rngStartNode = firstTextNode;
   335     rngStartOffset = 0;
   336   }
   338   nsCOMPtr<nsIDOMNode> lastTextNode = do_QueryInterface(lastText);
   339   NS_ENSURE_TRUE(lastTextNode, NS_ERROR_FAILURE);
   341   if (rngEndNode != lastTextNode)
   342   {
   343     // The range includes the end of the last text node!
   344     rngEndNode = lastTextNode;
   345     nsAutoString str;
   346     result = lastTextNode->GetNodeValue(str);
   347     rngEndOffset = str.Length();
   348   }
   350   // Create a doc iterator so that we can scan beyond
   351   // the bounds of the extent range.
   353   nsCOMPtr<nsIContentIterator> docIter;
   354   result = CreateDocumentContentIterator(getter_AddRefs(docIter));
   355   NS_ENSURE_SUCCESS(result, result);
   357   // Grab all the text in the block containing our
   358   // first text node.
   360   result = docIter->PositionAt(firstText);
   361   NS_ENSURE_SUCCESS(result, result);
   363   iterStatus = nsTextServicesDocument::eValid;
   365   nsTArray<OffsetEntry*> offsetTable;
   366   nsAutoString blockStr;
   368   result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
   369                              nullptr, &blockStr);
   370   if (NS_FAILED(result))
   371   {
   372     ClearOffsetTable(&offsetTable);
   373     return result;
   374   }
   376   nsCOMPtr<nsIDOMNode> wordStartNode, wordEndNode;
   377   int32_t wordStartOffset, wordEndOffset;
   379   result = FindWordBounds(&offsetTable, &blockStr,
   380                           rngStartNode, rngStartOffset,
   381                           getter_AddRefs(wordStartNode), &wordStartOffset,
   382                           getter_AddRefs(wordEndNode), &wordEndOffset);
   384   ClearOffsetTable(&offsetTable);
   386   NS_ENSURE_SUCCESS(result, result);
   388   rngStartNode = wordStartNode;
   389   rngStartOffset = wordStartOffset;
   391   // Grab all the text in the block containing our
   392   // last text node.
   394   result = docIter->PositionAt(lastText);
   395   NS_ENSURE_SUCCESS(result, result);
   397   iterStatus = nsTextServicesDocument::eValid;
   399   result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
   400                              nullptr, &blockStr);
   401   if (NS_FAILED(result))
   402   {
   403     ClearOffsetTable(&offsetTable);
   404     return result;
   405   }
   407   result = FindWordBounds(&offsetTable, &blockStr,
   408                           rngEndNode, rngEndOffset,
   409                           getter_AddRefs(wordStartNode), &wordStartOffset,
   410                           getter_AddRefs(wordEndNode), &wordEndOffset);
   412   ClearOffsetTable(&offsetTable);
   414   NS_ENSURE_SUCCESS(result, result);
   416   // To prevent expanding the range too much, we only change
   417   // rngEndNode and rngEndOffset if it isn't already at the start of the
   418   // word and isn't equivalent to rngStartNode and rngStartOffset.
   420   if (rngEndNode != wordStartNode || rngEndOffset != wordStartOffset ||
   421      (rngEndNode == rngStartNode  && rngEndOffset == rngStartOffset))
   422   {
   423     rngEndNode = wordEndNode;
   424     rngEndOffset = wordEndOffset;
   425   }
   427   // Now adjust the range so that it uses our new
   428   // end points.
   430   result = aRange->SetEnd(rngEndNode, rngEndOffset);
   431   NS_ENSURE_SUCCESS(result, result);
   433   return aRange->SetStart(rngStartNode, rngStartOffset);
   434 }
   436 NS_IMETHODIMP
   437 nsTextServicesDocument::SetFilter(nsITextServicesFilter *aFilter)
   438 {
   439   // Hang on to the filter so we can set it into the filtered iterator.
   440   mTxtSvcFilter = aFilter;
   442   return NS_OK;
   443 }
   445 NS_IMETHODIMP
   446 nsTextServicesDocument::GetCurrentTextBlock(nsString *aStr)
   447 {
   448   nsresult result;
   450   NS_ENSURE_TRUE(aStr, NS_ERROR_NULL_POINTER);
   452   aStr->Truncate();
   454   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
   456   LOCK_DOC(this);
   458   result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   459                              mExtent, aStr);
   461   UNLOCK_DOC(this);
   463   return result;
   464 }
   466 NS_IMETHODIMP
   467 nsTextServicesDocument::FirstBlock()
   468 {
   469   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
   471   LOCK_DOC(this);
   473   nsresult result = FirstTextNode(mIterator, &mIteratorStatus);
   475   if (NS_FAILED(result))
   476   {
   477     UNLOCK_DOC(this);
   478     return result;
   479   }
   481   // Keep track of prev and next blocks, just in case
   482   // the text service blows away the current block.
   484   if (mIteratorStatus == nsTextServicesDocument::eValid)
   485   {
   486     mPrevTextBlock  = nullptr;
   487     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
   488   }
   489   else
   490   {
   491     // There's no text block in the document!
   493     mPrevTextBlock  = nullptr;
   494     mNextTextBlock  = nullptr;
   495   }
   497   UNLOCK_DOC(this);
   499   return result;
   500 }
   502 NS_IMETHODIMP
   503 nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus,
   504                                           int32_t *aSelOffset,
   505                                           int32_t *aSelLength)
   506 {
   507   nsresult result = NS_OK;
   509   NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
   511   LOCK_DOC(this);
   513   mIteratorStatus = nsTextServicesDocument::eIsDone;
   515   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
   516   *aSelOffset = *aSelLength = -1;
   518   if (!mSelCon || !mIterator)
   519   {
   520     UNLOCK_DOC(this);
   521     return NS_ERROR_FAILURE;
   522   }
   524   nsCOMPtr<nsISelection> selection;
   525   bool isCollapsed = false;
   527   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
   529   if (NS_FAILED(result))
   530   {
   531     UNLOCK_DOC(this);
   532     return result;
   533   }
   535   result = selection->GetIsCollapsed(&isCollapsed);
   537   if (NS_FAILED(result))
   538   {
   539     UNLOCK_DOC(this);
   540     return result;
   541   }
   543   nsCOMPtr<nsIContentIterator> iter;
   544   nsCOMPtr<nsIDOMRange>        range;
   545   nsCOMPtr<nsIDOMNode>         parent;
   546   int32_t                      i, rangeCount, offset;
   548   if (isCollapsed)
   549   {
   550     // We have a caret. Check if the caret is in a text node.
   551     // If it is, make the text node's block the current block.
   552     // If the caret isn't in a text node, search forwards in
   553     // the document, till we find a text node.
   555     result = selection->GetRangeAt(0, getter_AddRefs(range));
   557     if (NS_FAILED(result))
   558     {
   559       UNLOCK_DOC(this);
   560       return result;
   561     }
   563     if (!range)
   564     {
   565       UNLOCK_DOC(this);
   566       return NS_ERROR_FAILURE;
   567     }
   569     result = range->GetStartContainer(getter_AddRefs(parent));
   571     if (NS_FAILED(result))
   572     {
   573       UNLOCK_DOC(this);
   574       return result;
   575     }
   577     if (!parent)
   578     {
   579       UNLOCK_DOC(this);
   580       return NS_ERROR_FAILURE;
   581     }
   583     result = range->GetStartOffset(&offset);
   585     if (NS_FAILED(result))
   586     {
   587       UNLOCK_DOC(this);
   588       return result;
   589     }
   591     if (IsTextNode(parent))
   592     {
   593       // The caret is in a text node. Find the beginning
   594       // of the text block containing this text node and
   595       // return.
   597       nsCOMPtr<nsIContent> content(do_QueryInterface(parent));
   599       if (!content)
   600       {
   601         UNLOCK_DOC(this);
   602         return NS_ERROR_FAILURE;
   603       }
   605       result = mIterator->PositionAt(content);
   607       if (NS_FAILED(result))
   608       {
   609         UNLOCK_DOC(this);
   610         return result;
   611       }
   613       result = FirstTextNodeInCurrentBlock(mIterator);
   615       if (NS_FAILED(result))
   616       {
   617         UNLOCK_DOC(this);
   618         return result;
   619       }
   621       mIteratorStatus = nsTextServicesDocument::eValid;
   623       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   624                                  mExtent, nullptr);
   626       if (NS_FAILED(result))
   627       {
   628         UNLOCK_DOC(this);
   629         return result;
   630       }
   632       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
   634       if (NS_FAILED(result))
   635       {
   636         UNLOCK_DOC(this);
   637         return result;
   638       }
   640       if (*aSelStatus == nsITextServicesDocument::eBlockContains)
   641         result = SetSelectionInternal(*aSelOffset, *aSelLength, false);
   642     }
   643     else
   644     {
   645       // The caret isn't in a text node. Create an iterator
   646       // based on a range that extends from the current caret
   647       // position to the end of the document, then walk forwards
   648       // till you find a text node, then find the beginning of it's block.
   650       result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, getter_AddRefs(range));
   652       if (NS_FAILED(result))
   653       {
   654         UNLOCK_DOC(this);
   655         return result;
   656       }
   658       result = range->GetCollapsed(&isCollapsed);
   660       if (NS_FAILED(result))
   661       {
   662         UNLOCK_DOC(this);
   663         return result;
   664       }
   666       if (isCollapsed)
   667       {
   668         // If we get here, the range is collapsed because there is nothing after
   669         // the caret! Just return NS_OK;
   671         UNLOCK_DOC(this);
   672         return NS_OK;
   673       }
   675       result = CreateContentIterator(range, getter_AddRefs(iter));
   677       if (NS_FAILED(result))
   678       {
   679         UNLOCK_DOC(this);
   680         return result;
   681       }
   683       iter->First();
   685       nsCOMPtr<nsIContent> content;
   686       while (!iter->IsDone())
   687       {
   688         content = do_QueryInterface(iter->GetCurrentNode());
   690         if (IsTextNode(content))
   691           break;
   693         content = nullptr;
   695         iter->Next();
   696       }
   698       if (!content)
   699       {
   700         UNLOCK_DOC(this);
   701         return NS_OK;
   702       }
   704       result = mIterator->PositionAt(content);
   706       if (NS_FAILED(result))
   707       {
   708         UNLOCK_DOC(this);
   709         return result;
   710       }
   712       result = FirstTextNodeInCurrentBlock(mIterator);
   714       if (NS_FAILED(result))
   715       {
   716         UNLOCK_DOC(this);
   717         return result;
   718       }
   720       mIteratorStatus = nsTextServicesDocument::eValid;
   722       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   723                                  mExtent, nullptr);
   725       if (NS_FAILED(result))
   726       {
   727         UNLOCK_DOC(this);
   728         return result;
   729       }
   731       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
   733       if (NS_FAILED(result))
   734       {
   735         UNLOCK_DOC(this);
   736         return result;
   737       }
   738     }
   740     UNLOCK_DOC(this);
   742     return result;
   743   }
   745   // If we get here, we have an uncollapsed selection!
   746   // Look backwards through each range in the selection till you
   747   // find the first text node. If you find one, find the
   748   // beginning of its text block, and make it the current
   749   // block.
   751   result = selection->GetRangeCount(&rangeCount);
   753   if (NS_FAILED(result))
   754   {
   755     UNLOCK_DOC(this);
   756     return result;
   757   }
   759   NS_ASSERTION(rangeCount > 0, "Unexpected range count!");
   761   if (rangeCount <= 0)
   762   {
   763     UNLOCK_DOC(this);
   764     return NS_OK;
   765   }
   767   // XXX: We may need to add some code here to make sure
   768   //      the ranges are sorted in document appearance order!
   770   for (i = rangeCount - 1; i >= 0; i--)
   771   {
   772     // Get the i'th range from the selection.
   774     result = selection->GetRangeAt(i, getter_AddRefs(range));
   776     if (NS_FAILED(result))
   777     {
   778       UNLOCK_DOC(this);
   779       return result;
   780     }
   782     // Create an iterator for the range.
   784     result = CreateContentIterator(range, getter_AddRefs(iter));
   786     if (NS_FAILED(result))
   787     {
   788       UNLOCK_DOC(this);
   789       return result;
   790     }
   792     iter->Last();
   794     // Now walk through the range till we find a text node.
   796     while (!iter->IsDone())
   797     {
   798       if (iter->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
   799         // We found a text node, so position the document's
   800         // iterator at the beginning of the block, then get
   801         // the selection in terms of the string offset.
   802         nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
   804         result = mIterator->PositionAt(content);
   806         if (NS_FAILED(result))
   807         {
   808           UNLOCK_DOC(this);
   809           return result;
   810         }
   812         result = FirstTextNodeInCurrentBlock(mIterator);
   814         if (NS_FAILED(result))
   815         {
   816           UNLOCK_DOC(this);
   817           return result;
   818         }
   820         mIteratorStatus = nsTextServicesDocument::eValid;
   822         result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   823                                    mExtent, nullptr);
   825         if (NS_FAILED(result))
   826         {
   827           UNLOCK_DOC(this);
   828           return result;
   829         }
   831         result = GetSelection(aSelStatus, aSelOffset, aSelLength);
   833         UNLOCK_DOC(this);
   835         return result;
   837       }
   839       iter->Prev();
   840     }
   841   }
   843   // If we get here, we didn't find any text node in the selection!
   844   // Create a range that extends from the end of the selection,
   845   // to the end of the document, then iterate forwards through
   846   // it till you find a text node!
   848   result = selection->GetRangeAt(rangeCount - 1, getter_AddRefs(range));
   850   if (NS_FAILED(result))
   851   {
   852     UNLOCK_DOC(this);
   853     return result;
   854   }
   856   if (!range)
   857   {
   858     UNLOCK_DOC(this);
   859     return NS_ERROR_FAILURE;
   860   }
   862   result = range->GetEndContainer(getter_AddRefs(parent));
   864   if (NS_FAILED(result))
   865   {
   866     UNLOCK_DOC(this);
   867     return result;
   868   }
   870   if (!parent)
   871   {
   872     UNLOCK_DOC(this);
   873     return NS_ERROR_FAILURE;
   874   }
   876   result = range->GetEndOffset(&offset);
   878   if (NS_FAILED(result))
   879   {
   880     UNLOCK_DOC(this);
   881     return result;
   882   }
   884   result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, getter_AddRefs(range));
   886   if (NS_FAILED(result))
   887   {
   888     UNLOCK_DOC(this);
   889     return result;
   890   }
   892   result = range->GetCollapsed(&isCollapsed);
   894   if (NS_FAILED(result))
   895   {
   896     UNLOCK_DOC(this);
   897     return result;
   898   }
   900   if (isCollapsed)
   901   {
   902     // If we get here, the range is collapsed because there is nothing after
   903     // the current selection! Just return NS_OK;
   905     UNLOCK_DOC(this);
   906     return NS_OK;
   907   }
   909   result = CreateContentIterator(range, getter_AddRefs(iter));
   911   if (NS_FAILED(result))
   912   {
   913     UNLOCK_DOC(this);
   914     return result;
   915   }
   917   iter->First();
   919   while (!iter->IsDone())
   920   {
   921     if (iter->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
   922       // We found a text node! Adjust the document's iterator to point
   923       // to the beginning of its text block, then get the current selection.
   924       nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
   926       result = mIterator->PositionAt(content);
   928       if (NS_FAILED(result))
   929       {
   930         UNLOCK_DOC(this);
   931         return result;
   932       }
   934       result = FirstTextNodeInCurrentBlock(mIterator);
   936       if (NS_FAILED(result))
   937       {
   938         UNLOCK_DOC(this);
   939         return result;
   940       }
   943       mIteratorStatus = nsTextServicesDocument::eValid;
   945       result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
   946                                  mExtent, nullptr);
   948       if (NS_FAILED(result))
   949       {
   950         UNLOCK_DOC(this);
   951         return result;
   952       }
   954       result = GetSelection(aSelStatus, aSelOffset, aSelLength);
   956       UNLOCK_DOC(this);
   958       return result;
   959     }
   961     iter->Next();
   962   }
   964   // If we get here, we didn't find any block before or inside
   965   // the selection! Just return OK.
   967   UNLOCK_DOC(this);
   969   return NS_OK;
   970 }
   972 NS_IMETHODIMP
   973 nsTextServicesDocument::PrevBlock()
   974 {
   975   nsresult result = NS_OK;
   977   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
   979   LOCK_DOC(this);
   981   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
   982     return NS_OK;
   984   switch (mIteratorStatus)
   985   {
   986     case nsTextServicesDocument::eValid:
   987     case nsTextServicesDocument::eNext:
   989       result = FirstTextNodeInPrevBlock(mIterator);
   991       if (NS_FAILED(result))
   992       {
   993         mIteratorStatus = nsTextServicesDocument::eIsDone;
   994         UNLOCK_DOC(this);
   995         return result;
   996       }
   998       if (mIterator->IsDone())
   999       {
  1000         mIteratorStatus = nsTextServicesDocument::eIsDone;
  1001         UNLOCK_DOC(this);
  1002         return NS_OK;
  1005       mIteratorStatus = nsTextServicesDocument::eValid;
  1006       break;
  1008     case nsTextServicesDocument::ePrev:
  1010       // The iterator already points to the previous
  1011       // block, so don't do anything.
  1013       mIteratorStatus = nsTextServicesDocument::eValid;
  1014       break;
  1016     default:
  1018       mIteratorStatus = nsTextServicesDocument::eIsDone;
  1019       break;
  1022   // Keep track of prev and next blocks, just in case
  1023   // the text service blows away the current block.
  1025   if (mIteratorStatus == nsTextServicesDocument::eValid)
  1027     result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
  1028     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
  1030   else
  1032     // We must be done!
  1034     mPrevTextBlock = nullptr;
  1035     mNextTextBlock = nullptr;
  1038   UNLOCK_DOC(this);
  1040   return result;
  1043 NS_IMETHODIMP
  1044 nsTextServicesDocument::NextBlock()
  1046   nsresult result = NS_OK;
  1048   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
  1050   LOCK_DOC(this);
  1052   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
  1053     return NS_OK;
  1055   switch (mIteratorStatus)
  1057     case nsTextServicesDocument::eValid:
  1059       // Advance the iterator to the next text block.
  1061       result = FirstTextNodeInNextBlock(mIterator);
  1063       if (NS_FAILED(result))
  1065         mIteratorStatus = nsTextServicesDocument::eIsDone;
  1066         UNLOCK_DOC(this);
  1067         return result;
  1070       if (mIterator->IsDone())
  1072         mIteratorStatus = nsTextServicesDocument::eIsDone;
  1073         UNLOCK_DOC(this);
  1074         return NS_OK;
  1077       mIteratorStatus = nsTextServicesDocument::eValid;
  1078       break;
  1080     case nsTextServicesDocument::eNext:
  1082       // The iterator already points to the next block,
  1083       // so don't do anything to it!
  1085       mIteratorStatus = nsTextServicesDocument::eValid;
  1086       break;
  1088     case nsTextServicesDocument::ePrev:
  1090       // If the iterator is pointing to the previous block,
  1091       // we know that there is no next text block! Just
  1092       // fall through to the default case!
  1094     default:
  1096       mIteratorStatus = nsTextServicesDocument::eIsDone;
  1097       break;
  1100   // Keep track of prev and next blocks, just in case
  1101   // the text service blows away the current block.
  1103   if (mIteratorStatus == nsTextServicesDocument::eValid)
  1105     result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
  1106     result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
  1108   else
  1110     // We must be done.
  1112     mPrevTextBlock = nullptr;
  1113     mNextTextBlock = nullptr;
  1117   UNLOCK_DOC(this);
  1119   return result;
  1122 NS_IMETHODIMP
  1123 nsTextServicesDocument::IsDone(bool *aIsDone)
  1125   NS_ENSURE_TRUE(aIsDone, NS_ERROR_NULL_POINTER);
  1127   *aIsDone = false;
  1129   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
  1131   LOCK_DOC(this);
  1133   *aIsDone = (mIteratorStatus == nsTextServicesDocument::eIsDone) ? true : false;
  1135   UNLOCK_DOC(this);
  1137   return NS_OK;
  1140 NS_IMETHODIMP
  1141 nsTextServicesDocument::SetSelection(int32_t aOffset, int32_t aLength)
  1143   nsresult result;
  1145   NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
  1147   LOCK_DOC(this);
  1149   result = SetSelectionInternal(aOffset, aLength, true);
  1151   UNLOCK_DOC(this);
  1153   //**** KDEBUG ****
  1154   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1155   //**** KDEBUG ****
  1157   return result;
  1160 NS_IMETHODIMP
  1161 nsTextServicesDocument::ScrollSelectionIntoView()
  1163   nsresult result;
  1165   NS_ENSURE_TRUE(mSelCon, NS_ERROR_FAILURE);
  1167   LOCK_DOC(this);
  1169   // After ScrollSelectionIntoView(), the pending notifications might be flushed
  1170   // and PresShell/PresContext/Frames may be dead. See bug 418470.
  1171   result = mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION,
  1172                                             nsISelectionController::SCROLL_SYNCHRONOUS);
  1174   UNLOCK_DOC(this);
  1176   return result;
  1179 NS_IMETHODIMP
  1180 nsTextServicesDocument::DeleteSelection()
  1182   nsresult result = NS_OK;
  1184   // We don't allow deletion during a collapsed selection!
  1185   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
  1186   NS_ASSERTION(editor, "DeleteSelection called without an editor present!"); 
  1187   NS_ASSERTION(SelectionIsValid(), "DeleteSelection called without a valid selection!"); 
  1189   if (!editor || !SelectionIsValid())
  1190     return NS_ERROR_FAILURE;
  1192   if (SelectionIsCollapsed())
  1193     return NS_OK;
  1195   LOCK_DOC(this);
  1197   //**** KDEBUG ****
  1198   // printf("\n---- Before Delete\n");
  1199   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1200   // PrintOffsetTable();
  1201   //**** KDEBUG ****
  1203   // If we have an mExtent, save off its current set of
  1204   // end points so we can compare them against mExtent's
  1205   // set after the deletion of the content.
  1207   nsCOMPtr<nsIDOMNode> origStartNode, origEndNode;
  1208   int32_t origStartOffset = 0, origEndOffset = 0;
  1210   if (mExtent)
  1212     result = GetRangeEndPoints(mExtent,
  1213                                getter_AddRefs(origStartNode), &origStartOffset,
  1214                                getter_AddRefs(origEndNode), &origEndOffset);
  1216     if (NS_FAILED(result))
  1218       UNLOCK_DOC(this);
  1219       return result;
  1223   int32_t i, selLength;
  1224   OffsetEntry *entry, *newEntry;
  1226   for (i = mSelStartIndex; i <= mSelEndIndex; i++)
  1228     entry = mOffsetTable[i];
  1230     if (i == mSelStartIndex)
  1232       // Calculate the length of the selection. Note that the
  1233       // selection length can be zero if the start of the selection
  1234       // is at the very end of a text node entry.
  1236       if (entry->mIsInsertedText)
  1238         // Inserted text offset entries have no width when
  1239         // talking in terms of string offsets! If the beginning
  1240         // of the selection is in an inserted text offset entry,
  1241         // the caret is always at the end of the entry!
  1243         selLength = 0;
  1245       else
  1246         selLength = entry->mLength - (mSelStartOffset - entry->mStrOffset);
  1248       if (selLength > 0 && mSelStartOffset > entry->mStrOffset)
  1250         // Selection doesn't start at the beginning of the
  1251         // text node entry. We need to split this entry into
  1252         // two pieces, the piece before the selection, and
  1253         // the piece inside the selection.
  1255         result = SplitOffsetEntry(i, selLength);
  1257         if (NS_FAILED(result))
  1259           UNLOCK_DOC(this);
  1260           return result;
  1263         // Adjust selection indexes to account for new entry:
  1265         ++mSelStartIndex;
  1266         ++mSelEndIndex;
  1267         ++i;
  1269         entry = mOffsetTable[i];
  1273       if (selLength > 0 && mSelStartIndex < mSelEndIndex)
  1275         // The entire entry is contained in the selection. Mark the
  1276         // entry invalid.
  1278         entry->mIsValid = false;
  1282   //**** KDEBUG ****
  1283   // printf("\n---- Middle Delete\n");
  1284   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1285   // PrintOffsetTable();
  1286   //**** KDEBUG ****
  1288     if (i == mSelEndIndex)
  1290       if (entry->mIsInsertedText)
  1292         // Inserted text offset entries have no width when
  1293         // talking in terms of string offsets! If the end
  1294         // of the selection is in an inserted text offset entry,
  1295         // the selection includes the entire entry!
  1297         entry->mIsValid = false;
  1299       else
  1301         // Calculate the length of the selection. Note that the
  1302         // selection length can be zero if the end of the selection
  1303         // is at the very beginning of a text node entry.
  1305         selLength = mSelEndOffset - entry->mStrOffset;
  1307         if (selLength > 0 && mSelEndOffset < entry->mStrOffset + entry->mLength)
  1309           // mStrOffset is guaranteed to be inside the selection, even
  1310           // when mSelStartIndex == mSelEndIndex.
  1312           result = SplitOffsetEntry(i, entry->mLength - selLength);
  1314           if (NS_FAILED(result))
  1316             UNLOCK_DOC(this);
  1317             return result;
  1320           // Update the entry fields:
  1322           newEntry = mOffsetTable[i+1];
  1323           newEntry->mNodeOffset = entry->mNodeOffset;
  1327         if (selLength > 0 && mSelEndOffset == entry->mStrOffset + entry->mLength)
  1329           // The entire entry is contained in the selection. Mark the
  1330           // entry invalid.
  1332           entry->mIsValid = false;
  1337     if (i != mSelStartIndex && i != mSelEndIndex)
  1339       // The entire entry is contained in the selection. Mark the
  1340       // entry invalid.
  1342       entry->mIsValid = false;
  1346   // Make sure mIterator always points to something valid!
  1348   AdjustContentIterator();
  1350   // Now delete the actual content!
  1352   result = editor->DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
  1354   if (NS_FAILED(result))
  1356     UNLOCK_DOC(this);
  1357     return result;
  1360   // Now that we've actually deleted the selected content,
  1361   // check to see if our mExtent has changed, if so, then
  1362   // we have to create a new content iterator!
  1364   if (origStartNode && origEndNode)
  1366     nsCOMPtr<nsIDOMNode> curStartNode, curEndNode;
  1367     int32_t curStartOffset = 0, curEndOffset = 0;
  1369     result = GetRangeEndPoints(mExtent,
  1370                                getter_AddRefs(curStartNode), &curStartOffset,
  1371                                getter_AddRefs(curEndNode), &curEndOffset);
  1373     if (NS_FAILED(result))
  1375       UNLOCK_DOC(this);
  1376       return result;
  1379     if (origStartNode != curStartNode || origEndNode != curEndNode)
  1381       // The range has changed, so we need to create a new content
  1382       // iterator based on the new range.
  1384       nsCOMPtr<nsIContent> curContent;
  1386       if (mIteratorStatus != nsTextServicesDocument::eIsDone) {
  1387         // The old iterator is still pointing to something valid,
  1388         // so get its current node so we can restore it after we
  1389         // create the new iterator!
  1391         curContent = mIterator->GetCurrentNode()
  1392                      ? mIterator->GetCurrentNode()->AsContent()
  1393                      : nullptr;
  1396       // Create the new iterator.
  1398       result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
  1400       if (NS_FAILED(result))
  1402         UNLOCK_DOC(this);
  1403         return result;
  1406       // Now make the new iterator point to the content node
  1407       // the old one was pointing at.
  1409       if (curContent)
  1411         result = mIterator->PositionAt(curContent);
  1413         if (NS_FAILED(result))
  1414           mIteratorStatus = eIsDone;
  1415         else
  1416           mIteratorStatus = eValid;
  1421   entry = 0;
  1423   // Move the caret to the end of the first valid entry.
  1424   // Start with mSelStartIndex since it may still be valid.
  1426   for (i = mSelStartIndex; !entry && i >= 0; i--)
  1428     entry = mOffsetTable[i];
  1430     if (!entry->mIsValid)
  1431       entry = 0;
  1432     else
  1434       mSelStartIndex  = mSelEndIndex  = i;
  1435       mSelStartOffset = mSelEndOffset = entry->mStrOffset + entry->mLength;
  1439   // If we still don't have a valid entry, move the caret
  1440   // to the next valid entry after the selection:
  1442   for (i = mSelEndIndex; !entry && i < int32_t(mOffsetTable.Length()); i++)
  1444     entry = mOffsetTable[i];
  1446     if (!entry->mIsValid)
  1447       entry = 0;
  1448     else
  1450       mSelStartIndex = mSelEndIndex = i;
  1451       mSelStartOffset = mSelEndOffset = entry->mStrOffset;
  1455   if (entry)
  1456     result = SetSelection(mSelStartOffset, 0);
  1457   else
  1459     // Uuughh we have no valid offset entry to place our
  1460     // caret ... just mark the selection invalid.
  1462     mSelStartIndex  = mSelEndIndex  = -1;
  1463     mSelStartOffset = mSelEndOffset = -1;
  1466   // Now remove any invalid entries from the offset table.
  1468   result = RemoveInvalidOffsetEntries();
  1470   //**** KDEBUG ****
  1471   // printf("\n---- After Delete\n");
  1472   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1473   // PrintOffsetTable();
  1474   //**** KDEBUG ****
  1476   UNLOCK_DOC(this);
  1478   return result;
  1481 NS_IMETHODIMP
  1482 nsTextServicesDocument::InsertText(const nsString *aText)
  1484   nsresult result = NS_OK;
  1486   nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
  1487   NS_ASSERTION(editor, "InsertText called without an editor present!"); 
  1489   if (!editor || !SelectionIsValid())
  1490     return NS_ERROR_FAILURE;
  1492   NS_ENSURE_TRUE(aText, NS_ERROR_NULL_POINTER);
  1494   // If the selection is not collapsed, we need to save
  1495   // off the selection offsets so we can restore the
  1496   // selection and delete the selected content after we've
  1497   // inserted the new text. This is necessary to try and
  1498   // retain as much of the original style of the content
  1499   // being deleted.
  1501   bool collapsedSelection = SelectionIsCollapsed();
  1502   int32_t savedSelOffset = mSelStartOffset;
  1503   int32_t savedSelLength = mSelEndOffset - mSelStartOffset;
  1505   if (!collapsedSelection)
  1507     // Collapse to the start of the current selection
  1508     // for the insert!
  1510     result = SetSelection(mSelStartOffset, 0);
  1512     NS_ENSURE_SUCCESS(result, result);
  1516   LOCK_DOC(this);
  1518   result = editor->BeginTransaction();
  1520   if (NS_FAILED(result))
  1522     UNLOCK_DOC(this);
  1523     return result;
  1526   nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(editor, &result));
  1527   if (textEditor)
  1528     result = textEditor->InsertText(*aText);
  1530   if (NS_FAILED(result))
  1532     editor->EndTransaction();
  1533     UNLOCK_DOC(this);
  1534     return result;
  1537   //**** KDEBUG ****
  1538   // printf("\n---- Before Insert\n");
  1539   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1540   // PrintOffsetTable();
  1541   //**** KDEBUG ****
  1543   int32_t strLength = aText->Length();
  1544   uint32_t i;
  1546   nsCOMPtr<nsISelection> selection;
  1547   OffsetEntry *itEntry;
  1548   OffsetEntry *entry = mOffsetTable[mSelStartIndex];
  1549   void *node         = entry->mNode;
  1551   NS_ASSERTION((entry->mIsValid), "Invalid insertion point!");
  1553   if (entry->mStrOffset == mSelStartOffset)
  1555     if (entry->mIsInsertedText)
  1557       // If the caret is in an inserted text offset entry,
  1558       // we simply insert the text at the end of the entry.
  1560       entry->mLength += strLength;
  1562     else
  1564       // Insert an inserted text offset entry before the current
  1565       // entry!
  1567       itEntry = new OffsetEntry(entry->mNode, entry->mStrOffset, strLength);
  1569       if (!itEntry)
  1571         editor->EndTransaction();
  1572         UNLOCK_DOC(this);
  1573         return NS_ERROR_OUT_OF_MEMORY;
  1576       itEntry->mIsInsertedText = true;
  1577       itEntry->mNodeOffset = entry->mNodeOffset;
  1579       if (!mOffsetTable.InsertElementAt(mSelStartIndex, itEntry))
  1581         editor->EndTransaction();
  1582         UNLOCK_DOC(this);
  1583         return NS_ERROR_FAILURE;
  1587   else if ((entry->mStrOffset + entry->mLength) == mSelStartOffset)
  1589     // We are inserting text at the end of the current offset entry.
  1590     // Look at the next valid entry in the table. If it's an inserted
  1591     // text entry, add to its length and adjust its node offset. If
  1592     // it isn't, add a new inserted text entry.
  1594     i       = mSelStartIndex + 1;
  1595     itEntry = 0;
  1597     if (mOffsetTable.Length() > i)
  1599       itEntry = mOffsetTable[i];
  1601       if (!itEntry)
  1603         editor->EndTransaction();
  1604         UNLOCK_DOC(this);
  1605         return NS_ERROR_FAILURE;
  1608       // Check if the entry is a match. If it isn't, set
  1609       // iEntry to zero.
  1611       if (!itEntry->mIsInsertedText || itEntry->mStrOffset != mSelStartOffset)
  1612         itEntry = 0;
  1615     if (!itEntry)
  1617       // We didn't find an inserted text offset entry, so
  1618       // create one.
  1620       itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, 0);
  1622       if (!itEntry)
  1624         editor->EndTransaction();
  1625         UNLOCK_DOC(this);
  1626         return NS_ERROR_OUT_OF_MEMORY;
  1629       itEntry->mNodeOffset = entry->mNodeOffset + entry->mLength;
  1630       itEntry->mIsInsertedText = true;
  1632       if (!mOffsetTable.InsertElementAt(i, itEntry))
  1634         delete itEntry;
  1635         return NS_ERROR_FAILURE;
  1639     // We have a valid inserted text offset entry. Update its
  1640     // length, adjust the selection indexes, and make sure the
  1641     // caret is properly placed!
  1643     itEntry->mLength += strLength;
  1645     mSelStartIndex = mSelEndIndex = i;
  1647     result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  1649     if (NS_FAILED(result))
  1651       editor->EndTransaction();
  1652       UNLOCK_DOC(this);
  1653       return result;
  1656     result = selection->Collapse(itEntry->mNode, itEntry->mNodeOffset + itEntry->mLength);
  1658     if (NS_FAILED(result))
  1660       editor->EndTransaction();
  1661       UNLOCK_DOC(this);
  1662       return result;
  1665   else if ((entry->mStrOffset + entry->mLength) > mSelStartOffset)
  1667     // We are inserting text into the middle of the current offset entry.
  1668     // split the current entry into two parts, then insert an inserted text
  1669     // entry between them!
  1671     i = entry->mLength - (mSelStartOffset - entry->mStrOffset);
  1673     result = SplitOffsetEntry(mSelStartIndex, i);
  1675     if (NS_FAILED(result))
  1677       editor->EndTransaction();
  1678       UNLOCK_DOC(this);
  1679       return result;
  1682     itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, strLength);
  1684     if (!itEntry)
  1686       editor->EndTransaction();
  1687       UNLOCK_DOC(this);
  1688       return NS_ERROR_OUT_OF_MEMORY;
  1691     itEntry->mIsInsertedText = true;
  1692     itEntry->mNodeOffset     = entry->mNodeOffset + entry->mLength;
  1694     if (!mOffsetTable.InsertElementAt(mSelStartIndex + 1, itEntry))
  1696       editor->EndTransaction();
  1697       UNLOCK_DOC(this);
  1698       return NS_ERROR_FAILURE;
  1701     mSelEndIndex = ++mSelStartIndex;
  1704   // We've just finished inserting an inserted text offset entry.
  1705   // update all entries with the same mNode pointer that follow
  1706   // it in the table!
  1708   for (i = mSelStartIndex + 1; i < mOffsetTable.Length(); i++)
  1710     entry = mOffsetTable[i];
  1712     if (entry->mNode == node)
  1714       if (entry->mIsValid)
  1715         entry->mNodeOffset += strLength;
  1717     else
  1718       break;
  1721   //**** KDEBUG ****
  1722   // printf("\n---- After Insert\n");
  1723   // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  1724   // PrintOffsetTable();
  1725   //**** KDEBUG ****
  1727   if (!collapsedSelection)
  1729     result = SetSelection(savedSelOffset, savedSelLength);
  1731     if (NS_FAILED(result))
  1733       editor->EndTransaction();
  1734       UNLOCK_DOC(this);
  1735       return result;
  1738     result = DeleteSelection();
  1740     if (NS_FAILED(result))
  1742       editor->EndTransaction();
  1743       UNLOCK_DOC(this);
  1744       return result;
  1748   result = editor->EndTransaction();
  1750   UNLOCK_DOC(this);
  1752   return result;
  1755 NS_IMETHODIMP
  1756 nsTextServicesDocument::DidInsertNode(nsIDOMNode *aNode,
  1757                                       nsIDOMNode *aParent,
  1758                                       int32_t     aPosition,
  1759                                       nsresult    aResult)
  1761   return NS_OK;
  1764 NS_IMETHODIMP
  1765 nsTextServicesDocument::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
  1767   NS_ENSURE_SUCCESS(aResult, NS_OK);
  1769   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
  1771   //**** KDEBUG ****
  1772   // printf("** DeleteNode: 0x%.8x\n", aChild);
  1773   // fflush(stdout);
  1774   //**** KDEBUG ****
  1776   LOCK_DOC(this);
  1778   int32_t nodeIndex = 0;
  1779   bool hasEntry = false;
  1780   OffsetEntry *entry;
  1782   nsresult result = NodeHasOffsetEntry(&mOffsetTable, aChild, &hasEntry, &nodeIndex);
  1784   if (NS_FAILED(result))
  1786     UNLOCK_DOC(this);
  1787     return result;
  1790   if (!hasEntry)
  1792     // It's okay if the node isn't in the offset table, the
  1793     // editor could be cleaning house.
  1795     UNLOCK_DOC(this);
  1796     return NS_OK;
  1799   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mIterator->GetCurrentNode());
  1801   if (node && node == aChild &&
  1802       mIteratorStatus != nsTextServicesDocument::eIsDone)
  1804     // XXX: This should never really happen because
  1805     // AdjustContentIterator() should have been called prior
  1806     // to the delete to try and position the iterator on the
  1807     // next valid text node in the offset table, and if there
  1808     // wasn't a next, it would've set mIteratorStatus to eIsDone.
  1810     NS_ERROR("DeleteNode called for current iterator node."); 
  1813   int32_t tcount = mOffsetTable.Length();
  1815   while (nodeIndex < tcount)
  1817     entry = mOffsetTable[nodeIndex];
  1819     if (!entry)
  1821       UNLOCK_DOC(this);
  1822       return NS_ERROR_FAILURE;
  1825     if (entry->mNode == aChild)
  1827       entry->mIsValid = false;
  1830     nodeIndex++;
  1833   UNLOCK_DOC(this);
  1835   return NS_OK;
  1838 NS_IMETHODIMP
  1839 nsTextServicesDocument::DidSplitNode(nsIDOMNode *aExistingRightNode,
  1840                                      int32_t     aOffset,
  1841                                      nsIDOMNode *aNewLeftNode,
  1842                                      nsresult    aResult)
  1844   //**** KDEBUG ****
  1845   // printf("** SplitNode: 0x%.8x  %d  0x%.8x\n", aExistingRightNode, aOffset, aNewLeftNode);
  1846   // fflush(stdout);
  1847   //**** KDEBUG ****
  1848   return NS_OK;
  1851 NS_IMETHODIMP
  1852 nsTextServicesDocument::DidJoinNodes(nsIDOMNode  *aLeftNode,
  1853                                      nsIDOMNode  *aRightNode,
  1854                                      nsIDOMNode  *aParent,
  1855                                      nsresult     aResult)
  1857   NS_ENSURE_SUCCESS(aResult, NS_OK);
  1859   int32_t i;
  1860   uint16_t type;
  1861   nsresult result;
  1863   //**** KDEBUG ****
  1864   // printf("** JoinNodes: 0x%.8x  0x%.8x  0x%.8x\n", aLeftNode, aRightNode, aParent);
  1865   // fflush(stdout);
  1866   //**** KDEBUG ****
  1868   // Make sure that both nodes are text nodes -- otherwise we don't care.
  1870   result = aLeftNode->GetNodeType(&type);
  1871   NS_ENSURE_SUCCESS(result, NS_OK);
  1872   if (nsIDOMNode::TEXT_NODE != type) {
  1873     return NS_OK;
  1876   result = aRightNode->GetNodeType(&type);
  1877   NS_ENSURE_SUCCESS(result, NS_OK);
  1878   if (nsIDOMNode::TEXT_NODE != type) {
  1879     return NS_OK;
  1882   // Note: The editor merges the contents of the left node into the
  1883   //       contents of the right.
  1885   int32_t leftIndex = 0;
  1886   int32_t rightIndex = 0;
  1887   bool leftHasEntry = false;
  1888   bool rightHasEntry = false;
  1890   result = NodeHasOffsetEntry(&mOffsetTable, aLeftNode, &leftHasEntry, &leftIndex);
  1892   NS_ENSURE_SUCCESS(result, result);
  1894   if (!leftHasEntry)
  1896     // It's okay if the node isn't in the offset table, the
  1897     // editor could be cleaning house.
  1898     return NS_OK;
  1901   result = NodeHasOffsetEntry(&mOffsetTable, aRightNode, &rightHasEntry, &rightIndex);
  1903   NS_ENSURE_SUCCESS(result, result);
  1905   if (!rightHasEntry)
  1907     // It's okay if the node isn't in the offset table, the
  1908     // editor could be cleaning house.
  1909     return NS_OK;
  1912   NS_ASSERTION(leftIndex < rightIndex, "Indexes out of order.");
  1914   if (leftIndex > rightIndex)
  1916     // Don't know how to handle this situation.
  1917     return NS_ERROR_FAILURE;
  1920   LOCK_DOC(this);
  1922   OffsetEntry *entry = mOffsetTable[rightIndex];
  1923   NS_ASSERTION(entry->mNodeOffset == 0, "Unexpected offset value for rightIndex.");
  1925   // Run through the table and change all entries referring to
  1926   // the left node so that they now refer to the right node:
  1928   nsAutoString str;
  1929   result = aLeftNode->GetNodeValue(str);
  1930   int32_t nodeLength = str.Length();
  1932   for (i = leftIndex; i < rightIndex; i++)
  1934     entry = mOffsetTable[i];
  1936     if (entry->mNode == aLeftNode)
  1938       if (entry->mIsValid)
  1939         entry->mNode = aRightNode;
  1941     else
  1942       break;
  1945   // Run through the table and adjust the node offsets
  1946   // for all entries referring to the right node.
  1948   for (i = rightIndex; i < int32_t(mOffsetTable.Length()); i++)
  1950     entry = mOffsetTable[i];
  1952     if (entry->mNode == aRightNode)
  1954       if (entry->mIsValid)
  1955         entry->mNodeOffset += nodeLength;
  1957     else
  1958       break;
  1961   // Now check to see if the iterator is pointing to the
  1962   // left node. If it is, make it point to the right node!
  1964   nsCOMPtr<nsIContent> leftContent = do_QueryInterface(aLeftNode);
  1965   nsCOMPtr<nsIContent> rightContent = do_QueryInterface(aRightNode);
  1967   if (!leftContent || !rightContent)
  1969     UNLOCK_DOC(this);
  1970     return NS_ERROR_FAILURE;
  1973   if (mIterator->GetCurrentNode() == leftContent)
  1974     result = mIterator->PositionAt(rightContent);
  1976   UNLOCK_DOC(this);
  1978   return NS_OK;
  1981 nsresult
  1982 nsTextServicesDocument::CreateContentIterator(nsIDOMRange *aRange, nsIContentIterator **aIterator)
  1984   nsresult result;
  1986   NS_ENSURE_TRUE(aRange && aIterator, NS_ERROR_NULL_POINTER);
  1988   *aIterator = 0;
  1990   // Create a nsFilteredContentIterator
  1991   // This class wraps the ContentIterator in order to give itself a chance 
  1992   // to filter out certain content nodes
  1993   nsFilteredContentIterator* filter = new nsFilteredContentIterator(mTxtSvcFilter);
  1994   *aIterator = static_cast<nsIContentIterator *>(filter);
  1995   if (*aIterator) {
  1996     NS_IF_ADDREF(*aIterator);
  1997     result = filter ? NS_OK : NS_ERROR_FAILURE;
  1998   } else {
  1999     delete filter;
  2000     result = NS_ERROR_FAILURE;
  2002   NS_ENSURE_SUCCESS(result, result);
  2004   NS_ENSURE_TRUE(*aIterator, NS_ERROR_NULL_POINTER);
  2006   result = (*aIterator)->Init(aRange);
  2008   if (NS_FAILED(result))
  2010     NS_RELEASE((*aIterator));
  2011     *aIterator = 0;
  2012     return result;
  2015   return NS_OK;
  2018 nsresult
  2019 nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode **aNode)
  2021   nsresult result;
  2023   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  2025   *aNode = 0;
  2027   NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
  2029   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDOMDocument);
  2031   if (htmlDoc)
  2033     // For HTML documents, the content root node is the body.
  2035     nsCOMPtr<nsIDOMHTMLElement> bodyElement;
  2037     result = htmlDoc->GetBody(getter_AddRefs(bodyElement));
  2039     NS_ENSURE_SUCCESS(result, result);
  2041     NS_ENSURE_TRUE(bodyElement, NS_ERROR_FAILURE);
  2043     result = bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
  2045   else
  2047     // For non-HTML documents, the content root node will be the document element.
  2049     nsCOMPtr<nsIDOMElement> docElement;
  2051     result = mDOMDocument->GetDocumentElement(getter_AddRefs(docElement));
  2053     NS_ENSURE_SUCCESS(result, result);
  2055     NS_ENSURE_TRUE(docElement, NS_ERROR_FAILURE);
  2057     result = docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
  2060   return result;
  2063 nsresult
  2064 nsTextServicesDocument::CreateDocumentContentRange(nsIDOMRange **aRange)
  2066   *aRange = nullptr;
  2068   nsCOMPtr<nsIDOMNode> node;
  2069   nsresult rv = GetDocumentContentRootNode(getter_AddRefs(node));
  2070   NS_ENSURE_SUCCESS(rv, rv);
  2071   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
  2073   nsCOMPtr<nsINode> nativeNode = do_QueryInterface(node);
  2074   NS_ENSURE_STATE(nativeNode);
  2076   nsRefPtr<nsRange> range = new nsRange(nativeNode);
  2078   rv = range->SelectNodeContents(node);
  2079   NS_ENSURE_SUCCESS(rv, rv);
  2081   range.forget(aRange);
  2082   return NS_OK;
  2085 nsresult
  2086 nsTextServicesDocument::CreateDocumentContentRootToNodeOffsetRange(nsIDOMNode *aParent, int32_t aOffset, bool aToStart, nsIDOMRange **aRange)
  2088   NS_ENSURE_TRUE(aParent && aRange, NS_ERROR_NULL_POINTER);
  2090   *aRange = 0;
  2092   NS_ASSERTION(aOffset >= 0, "Invalid offset!");
  2094   if (aOffset < 0)
  2095     return NS_ERROR_FAILURE;
  2097   nsCOMPtr<nsIDOMNode> bodyNode; 
  2098   nsresult rv = GetDocumentContentRootNode(getter_AddRefs(bodyNode));
  2099   NS_ENSURE_SUCCESS(rv, rv);
  2100   NS_ENSURE_TRUE(bodyNode, NS_ERROR_NULL_POINTER);
  2102   nsCOMPtr<nsIDOMNode> startNode;
  2103   nsCOMPtr<nsIDOMNode> endNode;
  2104   int32_t startOffset, endOffset;
  2106   if (aToStart) {
  2107     // The range should begin at the start of the document
  2108     // and extend up until (aParent, aOffset).
  2110     startNode   = bodyNode;
  2111     startOffset = 0;
  2112     endNode     = aParent;
  2113     endOffset   = aOffset;
  2114   } else {
  2115     // The range should begin at (aParent, aOffset) and
  2116     // extend to the end of the document.
  2118     startNode   = aParent;
  2119     startOffset = aOffset;
  2120     endNode     = bodyNode;
  2122     nsCOMPtr<nsINode> body = do_QueryInterface(bodyNode);
  2123     endOffset = body ? int32_t(body->GetChildCount()) : 0;
  2126   return nsRange::CreateRange(startNode, startOffset, endNode, endOffset,
  2127                               aRange);
  2130 nsresult
  2131 nsTextServicesDocument::CreateDocumentContentIterator(nsIContentIterator **aIterator)
  2133   nsresult result;
  2135   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
  2137   nsCOMPtr<nsIDOMRange> range;
  2139   result = CreateDocumentContentRange(getter_AddRefs(range));
  2141   NS_ENSURE_SUCCESS(result, result);
  2143   result = CreateContentIterator(range, aIterator);
  2145   return result;
  2148 nsresult
  2149 nsTextServicesDocument::AdjustContentIterator()
  2151   nsresult result = NS_OK;
  2153   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
  2155   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mIterator->GetCurrentNode()));
  2157   NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  2159   nsIDOMNode *nodePtr = node.get();
  2160   int32_t tcount      = mOffsetTable.Length();
  2162   nsIDOMNode *prevValidNode = 0;
  2163   nsIDOMNode *nextValidNode = 0;
  2164   bool foundEntry = false;
  2165   OffsetEntry *entry;
  2167   for (int32_t i = 0; i < tcount && !nextValidNode; i++)
  2169     entry = mOffsetTable[i];
  2171     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  2173     if (entry->mNode == nodePtr)
  2175       if (entry->mIsValid)
  2177         // The iterator is still pointing to something valid!
  2178         // Do nothing!
  2180         return NS_OK;
  2182       else
  2184         // We found an invalid entry that points to
  2185         // the current iterator node. Stop looking for
  2186         // a previous valid node!
  2188         foundEntry = true;
  2192     if (entry->mIsValid)
  2194       if (!foundEntry)
  2195         prevValidNode = entry->mNode;
  2196       else
  2197         nextValidNode = entry->mNode;
  2201   nsCOMPtr<nsIContent> content;
  2203   if (prevValidNode)
  2204     content = do_QueryInterface(prevValidNode);
  2205   else if (nextValidNode)
  2206     content = do_QueryInterface(nextValidNode);
  2208   if (content)
  2210     result = mIterator->PositionAt(content);
  2212     if (NS_FAILED(result))
  2213       mIteratorStatus = eIsDone;
  2214     else
  2215       mIteratorStatus = eValid;
  2217     return result;
  2220   // If we get here, there aren't any valid entries
  2221   // in the offset table! Try to position the iterator
  2222   // on the next text block first, then previous if
  2223   // one doesn't exist!
  2225   if (mNextTextBlock)
  2227     result = mIterator->PositionAt(mNextTextBlock);
  2229     if (NS_FAILED(result))
  2231       mIteratorStatus = eIsDone;
  2232       return result;
  2235     mIteratorStatus = eNext;
  2237   else if (mPrevTextBlock)
  2239     result = mIterator->PositionAt(mPrevTextBlock);
  2241     if (NS_FAILED(result))
  2243       mIteratorStatus = eIsDone;
  2244       return result;
  2247     mIteratorStatus = ePrev;
  2249   else
  2250     mIteratorStatus = eIsDone;
  2252   return NS_OK;
  2255 bool
  2256 nsTextServicesDocument::DidSkip(nsIContentIterator* aFilteredIter)
  2258   // We can assume here that the Iterator is a nsFilteredContentIterator because
  2259   // all the iterator are created in CreateContentIterator which create a 
  2260   // nsFilteredContentIterator
  2261   // So if the iterator bailed on one of the "filtered" content nodes then we 
  2262   // consider that to be a block and bail with true
  2263   if (aFilteredIter) {
  2264     nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
  2265     if (filter && filter->DidSkip()) {
  2266       return true;
  2269   return false;
  2272 void
  2273 nsTextServicesDocument::ClearDidSkip(nsIContentIterator* aFilteredIter)
  2275   // Clear filter's skip flag
  2276   if (aFilteredIter) {
  2277     nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
  2278     filter->ClearDidSkip();
  2282 bool
  2283 nsTextServicesDocument::IsBlockNode(nsIContent *aContent)
  2285   if (!aContent) {
  2286     NS_ERROR("How did a null pointer get passed to IsBlockNode?");
  2287     return false;
  2290   nsIAtom *atom = aContent->Tag();
  2292   return (sAAtom       != atom &&
  2293           sAddressAtom != atom &&
  2294           sBigAtom     != atom &&
  2295           sBAtom       != atom &&
  2296           sCiteAtom    != atom &&
  2297           sCodeAtom    != atom &&
  2298           sDfnAtom     != atom &&
  2299           sEmAtom      != atom &&
  2300           sFontAtom    != atom &&
  2301           sIAtom       != atom &&
  2302           sKbdAtom     != atom &&
  2303           sKeygenAtom  != atom &&
  2304           sNobrAtom    != atom &&
  2305           sSAtom       != atom &&
  2306           sSampAtom    != atom &&
  2307           sSmallAtom   != atom &&
  2308           sSpacerAtom  != atom &&
  2309           sSpanAtom    != atom &&
  2310           sStrikeAtom  != atom &&
  2311           sStrongAtom  != atom &&
  2312           sSubAtom     != atom &&
  2313           sSupAtom     != atom &&
  2314           sTtAtom      != atom &&
  2315           sUAtom       != atom &&
  2316           sVarAtom     != atom &&
  2317           sWbrAtom     != atom);
  2320 bool
  2321 nsTextServicesDocument::HasSameBlockNodeParent(nsIContent *aContent1, nsIContent *aContent2)
  2323   nsIContent* p1 = aContent1->GetParent();
  2324   nsIContent* p2 = aContent2->GetParent();
  2326   // Quick test:
  2328   if (p1 == p2)
  2329     return true;
  2331   // Walk up the parent hierarchy looking for closest block boundary node:
  2333   while (p1 && !IsBlockNode(p1))
  2335     p1 = p1->GetParent();
  2338   while (p2 && !IsBlockNode(p2))
  2340     p2 = p2->GetParent();
  2343   return p1 == p2;
  2346 bool
  2347 nsTextServicesDocument::IsTextNode(nsIContent *aContent)
  2349   NS_ENSURE_TRUE(aContent, false);
  2350   return nsIDOMNode::TEXT_NODE == aContent->NodeType();
  2353 bool
  2354 nsTextServicesDocument::IsTextNode(nsIDOMNode *aNode)
  2356   NS_ENSURE_TRUE(aNode, false);
  2358   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
  2359   return IsTextNode(content);
  2362 nsresult
  2363 nsTextServicesDocument::SetSelectionInternal(int32_t aOffset, int32_t aLength, bool aDoUpdate)
  2365   nsresult result = NS_OK;
  2367   NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
  2369   nsIDOMNode *sNode = 0, *eNode = 0;
  2370   int32_t i, sOffset = 0, eOffset = 0;
  2371   OffsetEntry *entry;
  2373   // Find start of selection in node offset terms:
  2375   for (i = 0; !sNode && i < int32_t(mOffsetTable.Length()); i++)
  2377     entry = mOffsetTable[i];
  2378     if (entry->mIsValid)
  2380       if (entry->mIsInsertedText)
  2382         // Caret can only be placed at the end of an
  2383         // inserted text offset entry, if the offsets
  2384         // match exactly!
  2386         if (entry->mStrOffset == aOffset)
  2388           sNode   = entry->mNode;
  2389           sOffset = entry->mNodeOffset + entry->mLength;
  2392       else if (aOffset >= entry->mStrOffset)
  2394         bool foundEntry = false;
  2395         int32_t strEndOffset = entry->mStrOffset + entry->mLength;
  2397         if (aOffset < strEndOffset)
  2398           foundEntry = true;
  2399         else if (aOffset == strEndOffset)
  2401           // Peek after this entry to see if we have any
  2402           // inserted text entries belonging to the same
  2403           // entry->mNode. If so, we have to place the selection
  2404           // after it!
  2406           if ((i+1) < int32_t(mOffsetTable.Length()))
  2408             OffsetEntry *nextEntry = mOffsetTable[i+1];
  2410             if (!nextEntry->mIsValid || nextEntry->mStrOffset != aOffset)
  2412               // Next offset entry isn't an exact match, so we'll
  2413               // just use the current entry.
  2414               foundEntry = true;
  2419         if (foundEntry)
  2421           sNode   = entry->mNode;
  2422           sOffset = entry->mNodeOffset + aOffset - entry->mStrOffset;
  2426       if (sNode)
  2428         mSelStartIndex  = i;
  2429         mSelStartOffset = aOffset;
  2434   NS_ENSURE_TRUE(sNode, NS_ERROR_FAILURE);
  2436   // XXX: If we ever get a SetSelection() method in nsIEditor, we should
  2437   //      use it.
  2439   nsCOMPtr<nsISelection> selection;
  2441   if (aDoUpdate)
  2443     result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  2445     NS_ENSURE_SUCCESS(result, result);
  2447     result = selection->Collapse(sNode, sOffset);
  2449     NS_ENSURE_SUCCESS(result, result);
  2452   if (aLength <= 0)
  2454     // We have a collapsed selection. (Caret)
  2456     mSelEndIndex  = mSelStartIndex;
  2457     mSelEndOffset = mSelStartOffset;
  2459    //**** KDEBUG ****
  2460    // printf("\n* Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  2461    //**** KDEBUG ****
  2463     return NS_OK;
  2466   // Find the end of the selection in node offset terms:
  2468   int32_t endOffset = aOffset + aLength;
  2470   for (i = mOffsetTable.Length() - 1; !eNode && i >= 0; i--)
  2472     entry = mOffsetTable[i];
  2474     if (entry->mIsValid)
  2476       if (entry->mIsInsertedText)
  2478         if (entry->mStrOffset == eOffset)
  2480           // If the selection ends on an inserted text offset entry,
  2481           // the selection includes the entire entry!
  2483           eNode   = entry->mNode;
  2484           eOffset = entry->mNodeOffset + entry->mLength;
  2487       else if (endOffset >= entry->mStrOffset && endOffset <= entry->mStrOffset + entry->mLength)
  2489         eNode   = entry->mNode;
  2490         eOffset = entry->mNodeOffset + endOffset - entry->mStrOffset;
  2493       if (eNode)
  2495         mSelEndIndex  = i;
  2496         mSelEndOffset = endOffset;
  2501   if (aDoUpdate && eNode)
  2503     result = selection->Extend(eNode, eOffset);
  2505     NS_ENSURE_SUCCESS(result, result);
  2508   //**** KDEBUG ****
  2509   // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
  2510   //**** KDEBUG ****
  2512   return result;
  2515 nsresult
  2516 nsTextServicesDocument::GetSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
  2518   nsresult result;
  2520   NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
  2522   *aSelStatus = nsITextServicesDocument::eBlockNotFound;
  2523   *aSelOffset = -1;
  2524   *aSelLength = -1;
  2526   NS_ENSURE_TRUE(mDOMDocument && mSelCon, NS_ERROR_FAILURE);
  2528   if (mIteratorStatus == nsTextServicesDocument::eIsDone)
  2529     return NS_OK;
  2531   nsCOMPtr<nsISelection> selection;
  2532   bool isCollapsed;
  2534   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  2536   NS_ENSURE_SUCCESS(result, result);
  2538   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  2540   result = selection->GetIsCollapsed(&isCollapsed);
  2542   NS_ENSURE_SUCCESS(result, result);
  2544   // XXX: If we expose this method publicly, we need to
  2545   //      add LOCK_DOC/UNLOCK_DOC calls!
  2547   // LOCK_DOC(this);
  2549   if (isCollapsed)
  2550     result = GetCollapsedSelection(aSelStatus, aSelOffset, aSelLength);
  2551   else
  2552     result = GetUncollapsedSelection(aSelStatus, aSelOffset, aSelLength);
  2554   // UNLOCK_DOC(this);
  2556   return result;
  2559 nsresult
  2560 nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
  2562   nsCOMPtr<nsISelection> selection;
  2563   nsresult result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  2564   NS_ENSURE_SUCCESS(result, result);
  2565   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  2567   // The calling function should have done the GetIsCollapsed()
  2568   // check already. Just assume it's collapsed!
  2569   *aSelStatus = nsITextServicesDocument::eBlockOutside;
  2570   *aSelOffset = *aSelLength = -1;
  2572   int32_t tableCount = mOffsetTable.Length();
  2574   if (tableCount == 0)
  2575     return NS_OK;
  2577   // Get pointers to the first and last offset entries
  2578   // in the table.
  2580   OffsetEntry* eStart = mOffsetTable[0];
  2581   OffsetEntry* eEnd;
  2582   if (tableCount > 1)
  2583     eEnd = mOffsetTable[tableCount - 1];
  2584   else
  2585     eEnd = eStart;
  2587   int32_t eStartOffset = eStart->mNodeOffset;
  2588   int32_t eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
  2590   nsCOMPtr<nsIDOMRange> range;
  2591   result = selection->GetRangeAt(0, getter_AddRefs(range));
  2592   NS_ENSURE_SUCCESS(result, result);
  2594   nsCOMPtr<nsIDOMNode> domParent;
  2595   result = range->GetStartContainer(getter_AddRefs(domParent));
  2596   NS_ENSURE_SUCCESS(result, result);
  2598   nsCOMPtr<nsINode> parent = do_QueryInterface(domParent);
  2599   MOZ_ASSERT(parent);
  2601   int32_t offset;
  2602   result = range->GetStartOffset(&offset);
  2603   NS_ENSURE_SUCCESS(result, result);
  2605   int32_t e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
  2606                                                domParent, offset);
  2607   int32_t e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
  2608                                                domParent, offset);
  2610   if (e1s1 > 0 || e2s1 < 0) {
  2611     // We're done if the caret is outside the current text block.
  2612     return NS_OK;
  2615   if (parent->NodeType() == nsIDOMNode::TEXT_NODE) {
  2616     // Good news, the caret is in a text node. Look
  2617     // through the offset table for the entry that
  2618     // matches its parent and offset.
  2620     for (int32_t i = 0; i < tableCount; i++) {
  2621       OffsetEntry* entry = mOffsetTable[i];
  2622       NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  2624       if (entry->mNode == domParent.get() &&
  2625           entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
  2627         *aSelStatus = nsITextServicesDocument::eBlockContains;
  2628         *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
  2629         *aSelLength = 0;
  2631         return NS_OK;
  2635     // If we get here, we didn't find a text node entry
  2636     // in our offset table that matched.
  2638     return NS_ERROR_FAILURE;
  2641   // The caret is in our text block, but it's positioned in some
  2642   // non-text node (ex. <b>). Create a range based on the start
  2643   // and end of the text block, then create an iterator based on
  2644   // this range, with its initial position set to the closest
  2645   // child of this non-text node. Then look for the closest text
  2646   // node.
  2648   result = CreateRange(eStart->mNode, eStartOffset, eEnd->mNode, eEndOffset, getter_AddRefs(range));
  2649   NS_ENSURE_SUCCESS(result, result);
  2651   nsCOMPtr<nsIContentIterator> iter;
  2652   result = CreateContentIterator(range, getter_AddRefs(iter));
  2653   NS_ENSURE_SUCCESS(result, result);
  2655   nsIContent* saveNode;
  2656   if (parent->HasChildren()) {
  2657     // XXX: We need to make sure that all of parent's
  2658     //      children are in the text block.
  2660     // If the parent has children, position the iterator
  2661     // on the child that is to the left of the offset.
  2663     uint32_t childIndex = (uint32_t)offset;
  2665     if (childIndex > 0) {
  2666       uint32_t numChildren = parent->GetChildCount();
  2667       NS_ASSERTION(childIndex <= numChildren, "Invalid selection offset!");
  2669       if (childIndex > numChildren) {
  2670         childIndex = numChildren;
  2673       childIndex -= 1;
  2676     nsIContent* content = parent->GetChildAt(childIndex);
  2677     NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
  2679     result = iter->PositionAt(content);
  2680     NS_ENSURE_SUCCESS(result, result);
  2682     saveNode = content;
  2683   } else {
  2684     // The parent has no children, so position the iterator
  2685     // on the parent.
  2686     NS_ENSURE_TRUE(parent->IsContent(), NS_ERROR_FAILURE);
  2687     nsCOMPtr<nsIContent> content = parent->AsContent();
  2689     result = iter->PositionAt(content);
  2690     NS_ENSURE_SUCCESS(result, result);
  2692     saveNode = content;
  2695   // Now iterate to the left, towards the beginning of
  2696   // the text block, to find the first text node you
  2697   // come across.
  2699   nsIContent* node = nullptr;
  2700   while (!iter->IsDone()) {
  2701     nsINode* current = iter->GetCurrentNode();
  2702     if (current->NodeType() == nsIDOMNode::TEXT_NODE) {
  2703       node = static_cast<nsIContent*>(current);
  2704       break;
  2707     iter->Prev();
  2710   if (node) {
  2711     // We found a node, now set the offset to the end
  2712     // of the text node.
  2713     offset = node->TextLength();
  2714   } else {
  2715     // We should never really get here, but I'm paranoid.
  2717     // We didn't find a text node above, so iterate to
  2718     // the right, towards the end of the text block, looking
  2719     // for a text node.
  2721     result = iter->PositionAt(saveNode);
  2722     NS_ENSURE_SUCCESS(result, result);
  2724     node = nullptr;
  2725     while (!iter->IsDone()) {
  2726       nsINode* current = iter->GetCurrentNode();
  2728       if (current->NodeType() == nsIDOMNode::TEXT_NODE) {
  2729         node = static_cast<nsIContent*>(current);
  2730         break;
  2733       iter->Next();
  2736     NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  2738     // We found a text node, so set the offset to
  2739     // the beginning of the node.
  2741     offset = 0;
  2744   for (int32_t i = 0; i < tableCount; i++) {
  2745     OffsetEntry* entry = mOffsetTable[i];
  2746     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  2748     if (entry->mNode == node->AsDOMNode() &&
  2749         entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
  2751       *aSelStatus = nsITextServicesDocument::eBlockContains;
  2752       *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
  2753       *aSelLength = 0;
  2755       // Now move the caret so that it is actually in the text node.
  2756       // We do this to keep things in sync.
  2757       //
  2758       // In most cases, the user shouldn't see any movement in the caret
  2759       // on screen.
  2761       result = SetSelectionInternal(*aSelOffset, *aSelLength, true);
  2763       return result;
  2767   return NS_ERROR_FAILURE;
  2770 nsresult
  2771 nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
  2773   nsresult result;
  2775   nsCOMPtr<nsISelection> selection;
  2776   nsCOMPtr<nsIDOMRange> range;
  2777   OffsetEntry *entry;
  2779   result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
  2781   NS_ENSURE_SUCCESS(result, result);
  2783   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
  2785   // It is assumed that the calling function has made sure that the
  2786   // selection is not collapsed, and that the input params to this
  2787   // method are initialized to some defaults.
  2789   nsCOMPtr<nsIDOMNode> startParent, endParent;
  2790   int32_t startOffset, endOffset;
  2791   int32_t rangeCount, tableCount, i;
  2792   int32_t e1s1, e1s2, e2s1, e2s2;
  2794   OffsetEntry *eStart, *eEnd;
  2795   int32_t eStartOffset, eEndOffset;
  2797   tableCount = mOffsetTable.Length();
  2799   // Get pointers to the first and last offset entries
  2800   // in the table.
  2802   eStart = mOffsetTable[0];
  2804   if (tableCount > 1)
  2805     eEnd = mOffsetTable[tableCount - 1];
  2806   else
  2807     eEnd = eStart;
  2809   eStartOffset = eStart->mNodeOffset;
  2810   eEndOffset   = eEnd->mNodeOffset + eEnd->mLength;
  2812   result = selection->GetRangeCount(&rangeCount);
  2814   NS_ENSURE_SUCCESS(result, result);
  2816   // Find the first range in the selection that intersects
  2817   // the current text block.
  2819   for (i = 0; i < rangeCount; i++)
  2821     result = selection->GetRangeAt(i, getter_AddRefs(range));
  2823     NS_ENSURE_SUCCESS(result, result);
  2825     result = GetRangeEndPoints(range,
  2826                                getter_AddRefs(startParent), &startOffset,
  2827                                getter_AddRefs(endParent), &endOffset);
  2829     NS_ENSURE_SUCCESS(result, result);
  2831     e1s2 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
  2832                                          endParent, endOffset);
  2833     e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
  2834                                          startParent, startOffset);
  2836     // Break out of the loop if the text block intersects the current range.
  2838     if (e1s2 <= 0 && e2s1 >= 0)
  2839       break;
  2842   // We're done if we didn't find an intersecting range.
  2844   if (rangeCount < 1 || e1s2 > 0 || e2s1 < 0)
  2846     *aSelStatus = nsITextServicesDocument::eBlockOutside;
  2847     *aSelOffset = *aSelLength = -1;
  2848     return NS_OK;
  2851   // Now that we have an intersecting range, find out more info:
  2853   e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
  2854                                        startParent, startOffset);
  2855   e2s2 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
  2856                                        endParent, endOffset);
  2858   if (rangeCount > 1)
  2860     // There are multiple selection ranges, we only deal
  2861     // with the first one that intersects the current,
  2862     // text block, so mark this a as a partial.
  2864     *aSelStatus = nsITextServicesDocument::eBlockPartial;
  2866   else if (e1s1 > 0 && e2s2 < 0)
  2868     // The range extends beyond the start and
  2869     // end of the current text block.
  2871     *aSelStatus = nsITextServicesDocument::eBlockInside;
  2873   else if (e1s1 <= 0 && e2s2 >= 0)
  2875     // The current text block contains the entire
  2876     // range.
  2878     *aSelStatus = nsITextServicesDocument::eBlockContains;
  2880   else
  2882     // The range partially intersects the block.
  2884     *aSelStatus = nsITextServicesDocument::eBlockPartial;
  2887   // Now create a range based on the intersection of the
  2888   // text block and range:
  2890   nsCOMPtr<nsIDOMNode> p1, p2;
  2891   int32_t     o1,  o2;
  2893   // The start of the range will be the rightmost
  2894   // start node.
  2896   if (e1s1 >= 0)
  2898     p1 = do_QueryInterface(eStart->mNode);
  2899     o1 = eStartOffset;
  2901   else
  2903     p1 = startParent;
  2904     o1 = startOffset;
  2907   // The end of the range will be the leftmost
  2908   // end node.
  2910   if (e2s2 <= 0)
  2912     p2 = do_QueryInterface(eEnd->mNode);
  2913     o2 = eEndOffset;
  2915   else
  2917     p2 = endParent;
  2918     o2 = endOffset;
  2921   result = CreateRange(p1, o1, p2, o2, getter_AddRefs(range));
  2923   NS_ENSURE_SUCCESS(result, result);
  2925   // Now iterate over this range to figure out the selection's
  2926   // block offset and length.
  2928   nsCOMPtr<nsIContentIterator> iter;
  2930   result = CreateContentIterator(range, getter_AddRefs(iter));
  2932   NS_ENSURE_SUCCESS(result, result);
  2934   // Find the first text node in the range.
  2936   bool found;
  2937   nsCOMPtr<nsIContent> content;
  2939   iter->First();
  2941   if (!IsTextNode(p1))
  2943     found = false;
  2945     while (!iter->IsDone())
  2947       content = do_QueryInterface(iter->GetCurrentNode());
  2949       if (IsTextNode(content))
  2951         p1 = do_QueryInterface(content);
  2953         NS_ENSURE_TRUE(p1, NS_ERROR_FAILURE);
  2955         o1 = 0;
  2956         found = true;
  2958         break;
  2961       iter->Next();
  2964     NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
  2967   // Find the last text node in the range.
  2969   iter->Last();
  2971   if (! IsTextNode(p2))
  2973     found = false;
  2975     while (!iter->IsDone())
  2977       content = do_QueryInterface(iter->GetCurrentNode());
  2979       if (IsTextNode(content))
  2981         p2 = do_QueryInterface(content);
  2983         NS_ENSURE_TRUE(p2, NS_ERROR_FAILURE);
  2985         nsString str;
  2987         result = p2->GetNodeValue(str);
  2989         NS_ENSURE_SUCCESS(result, result);
  2991         o2 = str.Length();
  2992         found = true;
  2994         break;
  2997       iter->Prev();
  3000     NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
  3003   found    = false;
  3004   *aSelLength = 0;
  3006   for (i = 0; i < tableCount; i++)
  3008     entry = mOffsetTable[i];
  3010     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  3012     if (!found)
  3014       if (entry->mNode == p1.get() &&
  3015           entry->mNodeOffset <= o1 && o1 <= (entry->mNodeOffset + entry->mLength))
  3017         *aSelOffset = entry->mStrOffset + (o1 - entry->mNodeOffset);
  3019         if (p1 == p2 &&
  3020             entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
  3022           // The start and end of the range are in the same offset
  3023           // entry. Calculate the length of the range then we're done.
  3025           *aSelLength = o2 - o1;
  3026           break;
  3028         else
  3030           // Add the length of the sub string in this offset entry
  3031           // that follows the start of the range.
  3033           *aSelLength = entry->mLength - (o1 - entry->mNodeOffset);
  3036         found = true;
  3039     else // found
  3041       if (entry->mNode == p2.get() &&
  3042           entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
  3044         // We found the end of the range. Calculate the length of the
  3045         // sub string that is before the end of the range, then we're done.
  3047         *aSelLength += o2 - entry->mNodeOffset;
  3048         break;
  3050       else
  3052         // The entire entry must be in the range.
  3054         *aSelLength += entry->mLength;
  3059   return result;
  3062 bool
  3063 nsTextServicesDocument::SelectionIsCollapsed()
  3065   return(mSelStartIndex == mSelEndIndex && mSelStartOffset == mSelEndOffset);
  3068 bool
  3069 nsTextServicesDocument::SelectionIsValid()
  3071   return(mSelStartIndex >= 0);
  3074 nsresult
  3075 nsTextServicesDocument::GetRangeEndPoints(nsIDOMRange *aRange,
  3076                                           nsIDOMNode **aStartParent, int32_t *aStartOffset,
  3077                                           nsIDOMNode **aEndParent, int32_t *aEndOffset)
  3079   nsresult result;
  3081   NS_ENSURE_TRUE(aRange && aStartParent && aStartOffset && aEndParent && aEndOffset, NS_ERROR_NULL_POINTER);
  3083   result = aRange->GetStartContainer(aStartParent);
  3085   NS_ENSURE_SUCCESS(result, result);
  3087   NS_ENSURE_TRUE(aStartParent, NS_ERROR_FAILURE);
  3089   result = aRange->GetStartOffset(aStartOffset);
  3091   NS_ENSURE_SUCCESS(result, result);
  3093   result = aRange->GetEndContainer(aEndParent);
  3095   NS_ENSURE_SUCCESS(result, result);
  3097   NS_ENSURE_TRUE(aEndParent, NS_ERROR_FAILURE);
  3099   result = aRange->GetEndOffset(aEndOffset);
  3101   return result;
  3105 nsresult
  3106 nsTextServicesDocument::CreateRange(nsIDOMNode *aStartParent, int32_t aStartOffset,
  3107                                     nsIDOMNode *aEndParent, int32_t aEndOffset,
  3108                                     nsIDOMRange **aRange)
  3110   return nsRange::CreateRange(aStartParent, aStartOffset, aEndParent,
  3111                               aEndOffset, aRange);
  3114 nsresult
  3115 nsTextServicesDocument::FirstTextNode(nsIContentIterator *aIterator,
  3116                                       TSDIteratorStatus *aIteratorStatus)
  3118   if (aIteratorStatus)
  3119     *aIteratorStatus = nsTextServicesDocument::eIsDone;
  3121   aIterator->First();
  3123   while (!aIterator->IsDone()) {
  3124     if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
  3125       if (aIteratorStatus)
  3126         *aIteratorStatus = nsTextServicesDocument::eValid;
  3127       break;
  3130     aIterator->Next();
  3133   return NS_OK;
  3136 nsresult
  3137 nsTextServicesDocument::LastTextNode(nsIContentIterator *aIterator,
  3138                                      TSDIteratorStatus *aIteratorStatus)
  3140   if (aIteratorStatus)
  3141     *aIteratorStatus = nsTextServicesDocument::eIsDone;
  3143   aIterator->Last();
  3145   while (!aIterator->IsDone()) {
  3146     if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
  3147       if (aIteratorStatus)
  3148         *aIteratorStatus = nsTextServicesDocument::eValid;
  3149       break;
  3152     aIterator->Prev();
  3155   return NS_OK;
  3158 nsresult
  3159 nsTextServicesDocument::FirstTextNodeInCurrentBlock(nsIContentIterator *iter)
  3161   NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
  3163   ClearDidSkip(iter);
  3165   nsCOMPtr<nsIContent> last;
  3167   // Walk backwards over adjacent text nodes until
  3168   // we hit a block boundary:
  3170   while (!iter->IsDone())
  3172     nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->IsContent()
  3173                                    ? iter->GetCurrentNode()->AsContent()
  3174                                    : nullptr;
  3176     if (IsTextNode(content))
  3178       if (!last || HasSameBlockNodeParent(content, last))
  3179         last = content;
  3180       else
  3182         // We're done, the current text node is in a
  3183         // different block.
  3184         break;
  3187     else if (last && IsBlockNode(content))
  3188       break;
  3190     iter->Prev();
  3192     if (DidSkip(iter))
  3193       break;
  3196   if (last)
  3197     iter->PositionAt(last);
  3199   // XXX: What should we return if last is null?
  3201   return NS_OK;
  3204 nsresult
  3205 nsTextServicesDocument::FirstTextNodeInPrevBlock(nsIContentIterator *aIterator)
  3207   nsCOMPtr<nsIContent> content;
  3208   nsresult result;
  3210   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
  3212   // XXX: What if mIterator is not currently on a text node?
  3214   // Make sure mIterator is pointing to the first text node in the
  3215   // current block:
  3217   result = FirstTextNodeInCurrentBlock(aIterator);
  3219   NS_ENSURE_SUCCESS(result, NS_ERROR_FAILURE);
  3221   // Point mIterator to the first node before the first text node:
  3223   aIterator->Prev();
  3225   if (aIterator->IsDone())
  3226     return NS_ERROR_FAILURE;
  3228   // Now find the first text node of the next block:
  3230   return FirstTextNodeInCurrentBlock(aIterator);
  3233 nsresult
  3234 nsTextServicesDocument::FirstTextNodeInNextBlock(nsIContentIterator *aIterator)
  3236   nsCOMPtr<nsIContent> prev;
  3237   bool crossedBlockBoundary = false;
  3239   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
  3241   ClearDidSkip(aIterator);
  3243   while (!aIterator->IsDone())
  3245     nsCOMPtr<nsIContent> content = aIterator->GetCurrentNode()->IsContent()
  3246                                    ? aIterator->GetCurrentNode()->AsContent()
  3247                                    : nullptr;
  3249     if (IsTextNode(content))
  3251       if (!crossedBlockBoundary && (!prev || HasSameBlockNodeParent(prev, content)))
  3252         prev = content;
  3253       else
  3254         break;
  3256     else if (!crossedBlockBoundary && IsBlockNode(content))
  3257       crossedBlockBoundary = true;
  3259     aIterator->Next();
  3261     if (!crossedBlockBoundary && DidSkip(aIterator))
  3262       crossedBlockBoundary = true;
  3265   return NS_OK;
  3268 nsresult
  3269 nsTextServicesDocument::GetFirstTextNodeInPrevBlock(nsIContent **aContent)
  3271   nsresult result;
  3273   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
  3275   *aContent = 0;
  3277   // Save the iterator's current content node so we can restore
  3278   // it when we are done:
  3280   nsINode* node = mIterator->GetCurrentNode();
  3282   result = FirstTextNodeInPrevBlock(mIterator);
  3284   if (NS_FAILED(result))
  3286     // Try to restore the iterator before returning.
  3287     mIterator->PositionAt(node);
  3288     return result;
  3291   if (!mIterator->IsDone())
  3293     nsCOMPtr<nsIContent> current = mIterator->GetCurrentNode()->IsContent()
  3294                                    ? mIterator->GetCurrentNode()->AsContent()
  3295                                    : nullptr;
  3296     current.forget(aContent);
  3299   // Restore the iterator:
  3301   return mIterator->PositionAt(node);
  3304 nsresult
  3305 nsTextServicesDocument::GetFirstTextNodeInNextBlock(nsIContent **aContent)
  3307   nsresult result;
  3309   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
  3311   *aContent = 0;
  3313   // Save the iterator's current content node so we can restore
  3314   // it when we are done:
  3316   nsINode* node = mIterator->GetCurrentNode();
  3318   result = FirstTextNodeInNextBlock(mIterator);
  3320   if (NS_FAILED(result))
  3322     // Try to restore the iterator before returning.
  3323     mIterator->PositionAt(node);
  3324     return result;
  3327   if (!mIterator->IsDone())
  3329     nsCOMPtr<nsIContent> current = mIterator->GetCurrentNode()->IsContent()
  3330                                    ? mIterator->GetCurrentNode()->AsContent()
  3331                                    : nullptr;
  3332     current.forget(aContent);
  3335   // Restore the iterator:
  3336   return mIterator->PositionAt(node);
  3339 nsresult
  3340 nsTextServicesDocument::CreateOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable,
  3341                                           nsIContentIterator *aIterator,
  3342                                           TSDIteratorStatus *aIteratorStatus,
  3343                                           nsIDOMRange *aIterRange,
  3344                                           nsString *aStr)
  3346   nsresult result = NS_OK;
  3348   nsCOMPtr<nsIContent> first;
  3349   nsCOMPtr<nsIContent> prev;
  3351   NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
  3353   ClearOffsetTable(aOffsetTable);
  3355   if (aStr)
  3356     aStr->Truncate();
  3358   if (*aIteratorStatus == nsTextServicesDocument::eIsDone)
  3359     return NS_OK;
  3361   // If we have an aIterRange, retrieve the endpoints so
  3362   // they can be used in the while loop below to trim entries
  3363   // for text nodes that are partially selected by aIterRange.
  3365   nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
  3366   int32_t rngStartOffset = 0, rngEndOffset = 0;
  3368   if (aIterRange)
  3370     result = GetRangeEndPoints(aIterRange,
  3371                                getter_AddRefs(rngStartNode), &rngStartOffset,
  3372                                getter_AddRefs(rngEndNode), &rngEndOffset);
  3374     NS_ENSURE_SUCCESS(result, result);
  3377   // The text service could have added text nodes to the beginning
  3378   // of the current block and called this method again. Make sure
  3379   // we really are at the beginning of the current block:
  3381   result = FirstTextNodeInCurrentBlock(aIterator);
  3383   NS_ENSURE_SUCCESS(result, result);
  3385   int32_t offset = 0;
  3387   ClearDidSkip(aIterator);
  3389   while (!aIterator->IsDone())
  3391     nsCOMPtr<nsIContent> content = aIterator->GetCurrentNode()->IsContent()
  3392                                    ? aIterator->GetCurrentNode()->AsContent()
  3393                                    : nullptr;
  3395     if (IsTextNode(content))
  3397       if (!prev || HasSameBlockNodeParent(prev, content))
  3399         nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
  3401         if (node)
  3403           nsString str;
  3405           result = node->GetNodeValue(str);
  3407           NS_ENSURE_SUCCESS(result, result);
  3409           // Add an entry for this text node into the offset table:
  3411           OffsetEntry *entry = new OffsetEntry(node, offset, str.Length());
  3412           aOffsetTable->AppendElement(entry);
  3414           // If one or both of the endpoints of the iteration range
  3415           // are in the text node for this entry, make sure the entry
  3416           // only accounts for the portion of the text node that is
  3417           // in the range.
  3419           int32_t startOffset = 0;
  3420           int32_t endOffset   = str.Length();
  3421           bool adjustStr    = false;
  3423           if (entry->mNode == rngStartNode)
  3425             entry->mNodeOffset = startOffset = rngStartOffset;
  3426             adjustStr = true;
  3429           if (entry->mNode == rngEndNode)
  3431             endOffset = rngEndOffset;
  3432             adjustStr = true;
  3435           if (adjustStr)
  3437             entry->mLength = endOffset - startOffset;
  3438             str = Substring(str, startOffset, entry->mLength);
  3441           offset += str.Length();
  3443           if (aStr)
  3445             // Append the text node's string to the output string:
  3447             if (!first)
  3448               *aStr = str;
  3449             else
  3450               *aStr += str;
  3454         prev = content;
  3456         if (!first)
  3457           first = content;
  3459       else
  3460         break;
  3463     else if (IsBlockNode(content))
  3464       break;
  3466     aIterator->Next();
  3468     if (DidSkip(aIterator))
  3469       break;
  3472   if (first)
  3474     // Always leave the iterator pointing at the first
  3475     // text node of the current block!
  3477     aIterator->PositionAt(first);
  3479   else
  3481     // If we never ran across a text node, the iterator
  3482     // might have been pointing to something invalid to
  3483     // begin with.
  3485     *aIteratorStatus = nsTextServicesDocument::eIsDone;
  3488   return result;
  3491 nsresult
  3492 nsTextServicesDocument::RemoveInvalidOffsetEntries()
  3494   OffsetEntry *entry;
  3495   int32_t i = 0;
  3497   while (uint32_t(i) < mOffsetTable.Length())
  3499     entry = mOffsetTable[i];
  3501     if (!entry->mIsValid)
  3503       mOffsetTable.RemoveElementAt(i);
  3505       if (mSelStartIndex >= 0 && mSelStartIndex >= i)
  3507         // We are deleting an entry that comes before
  3508         // mSelStartIndex, decrement mSelStartIndex so
  3509         // that it points to the correct entry!
  3511         NS_ASSERTION(i != mSelStartIndex, "Invalid selection index.");
  3513         --mSelStartIndex;
  3514         --mSelEndIndex;
  3517     else
  3518       i++;
  3521   return NS_OK;
  3524 nsresult
  3525 nsTextServicesDocument::ClearOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable)
  3527   uint32_t i;
  3529   for (i = 0; i < aOffsetTable->Length(); i++)
  3531     delete aOffsetTable->ElementAt(i);
  3534   aOffsetTable->Clear();
  3536   return NS_OK;
  3539 nsresult
  3540 nsTextServicesDocument::SplitOffsetEntry(int32_t aTableIndex, int32_t aNewEntryLength)
  3542   OffsetEntry *entry = mOffsetTable[aTableIndex];
  3544   NS_ASSERTION((aNewEntryLength > 0), "aNewEntryLength <= 0");
  3545   NS_ASSERTION((aNewEntryLength < entry->mLength), "aNewEntryLength >= mLength");
  3547   if (aNewEntryLength < 1 || aNewEntryLength >= entry->mLength)
  3548     return NS_ERROR_FAILURE;
  3550   int32_t oldLength = entry->mLength - aNewEntryLength;
  3552   OffsetEntry *newEntry = new OffsetEntry(entry->mNode,
  3553                                           entry->mStrOffset + oldLength,
  3554                                           aNewEntryLength);
  3556   if (!mOffsetTable.InsertElementAt(aTableIndex + 1, newEntry))
  3558     delete newEntry;
  3559     return NS_ERROR_FAILURE;
  3562    // Adjust entry fields:
  3564    entry->mLength        = oldLength;
  3565    newEntry->mNodeOffset = entry->mNodeOffset + oldLength;
  3567   return NS_OK;
  3570 nsresult
  3571 nsTextServicesDocument::NodeHasOffsetEntry(nsTArray<OffsetEntry*> *aOffsetTable, nsIDOMNode *aNode, bool *aHasEntry, int32_t *aEntryIndex)
  3573   OffsetEntry *entry;
  3574   uint32_t i;
  3576   NS_ENSURE_TRUE(aNode && aHasEntry && aEntryIndex, NS_ERROR_NULL_POINTER);
  3578   for (i = 0; i < aOffsetTable->Length(); i++)
  3580     entry = (*aOffsetTable)[i];
  3582     NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
  3584     if (entry->mNode == aNode)
  3586       *aHasEntry   = true;
  3587       *aEntryIndex = i;
  3589       return NS_OK;
  3593   *aHasEntry   = false;
  3594   *aEntryIndex = -1;
  3596   return NS_OK;
  3599 // Spellchecker code has this. See bug 211343
  3600 #define IS_NBSP_CHAR(c) (((unsigned char)0xa0)==(c))
  3602 nsresult
  3603 nsTextServicesDocument::FindWordBounds(nsTArray<OffsetEntry*> *aOffsetTable,
  3604                                        nsString *aBlockStr,
  3605                                        nsIDOMNode *aNode,
  3606                                        int32_t aNodeOffset,
  3607                                        nsIDOMNode **aWordStartNode,
  3608                                        int32_t *aWordStartOffset,
  3609                                        nsIDOMNode **aWordEndNode,
  3610                                        int32_t *aWordEndOffset)
  3612   // Initialize return values.
  3614   if (aWordStartNode)
  3615     *aWordStartNode = nullptr;
  3616   if (aWordStartOffset)
  3617     *aWordStartOffset = 0;
  3618   if (aWordEndNode)
  3619     *aWordEndNode = nullptr;
  3620   if (aWordEndOffset)
  3621     *aWordEndOffset = 0;
  3623   int32_t entryIndex = 0;
  3624   bool hasEntry = false;
  3626   // It's assumed that aNode is a text node. The first thing
  3627   // we do is get its index in the offset table so we can
  3628   // calculate the dom point's string offset.
  3630   nsresult result = NodeHasOffsetEntry(aOffsetTable, aNode, &hasEntry, &entryIndex);
  3631   NS_ENSURE_SUCCESS(result, result);
  3632   NS_ENSURE_TRUE(hasEntry, NS_ERROR_FAILURE);
  3634   // Next we map aNodeOffset into a string offset.
  3636   OffsetEntry *entry = (*aOffsetTable)[entryIndex];
  3637   uint32_t strOffset = entry->mStrOffset + aNodeOffset - entry->mNodeOffset;
  3639   // Now we use the word breaker to find the beginning and end
  3640   // of the word from our calculated string offset.
  3642   const char16_t *str = aBlockStr->get();
  3643   uint32_t strLen = aBlockStr->Length();
  3645   nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
  3646   nsWordRange res = wordBreaker->FindWord(str, strLen, strOffset);
  3647   if (res.mBegin > strLen) {
  3648     return str ? NS_ERROR_ILLEGAL_VALUE : NS_ERROR_NULL_POINTER;
  3651   // Strip out the NBSPs at the ends
  3652   while ((res.mBegin <= res.mEnd) && (IS_NBSP_CHAR(str[res.mBegin]))) 
  3653     res.mBegin++;
  3654   if (str[res.mEnd] == (unsigned char)0x20)
  3656     uint32_t realEndWord = res.mEnd - 1;
  3657     while ((realEndWord > res.mBegin) && (IS_NBSP_CHAR(str[realEndWord]))) 
  3658       realEndWord--;
  3659     if (realEndWord < res.mEnd - 1) 
  3660       res.mEnd = realEndWord + 1;
  3663   // Now that we have the string offsets for the beginning
  3664   // and end of the word, run through the offset table and
  3665   // convert them back into dom points.
  3667   int32_t i, lastIndex = aOffsetTable->Length() - 1;
  3669   for (i=0; i <= lastIndex; i++)
  3671     entry = (*aOffsetTable)[i];
  3673     int32_t strEndOffset = entry->mStrOffset + entry->mLength;
  3675     // Check to see if res.mBegin is within the range covered
  3676     // by this entry. Note that if res.mBegin is after the last
  3677     // character covered by this entry, we will use the next
  3678     // entry if there is one.
  3680     if (uint32_t(entry->mStrOffset) <= res.mBegin &&
  3681         (res.mBegin < uint32_t(strEndOffset) ||
  3682         (res.mBegin == uint32_t(strEndOffset) && i == lastIndex)))
  3684       if (aWordStartNode)
  3686         *aWordStartNode = entry->mNode;
  3687         NS_IF_ADDREF(*aWordStartNode);
  3690       if (aWordStartOffset)
  3691         *aWordStartOffset = entry->mNodeOffset + res.mBegin - entry->mStrOffset;
  3693       if (!aWordEndNode && !aWordEndOffset)
  3695         // We've found our start entry, but if we're not looking
  3696         // for end entries, we're done.
  3698         break;
  3702     // Check to see if res.mEnd is within the range covered
  3703     // by this entry.
  3705     if (uint32_t(entry->mStrOffset) <= res.mEnd && res.mEnd <= uint32_t(strEndOffset))
  3707       if (res.mBegin == res.mEnd && res.mEnd == uint32_t(strEndOffset) && i != lastIndex)
  3709         // Wait for the next round so that we use the same entry
  3710         // we did for aWordStartNode.
  3712         continue;
  3715       if (aWordEndNode)
  3717         *aWordEndNode = entry->mNode;
  3718         NS_IF_ADDREF(*aWordEndNode);
  3721       if (aWordEndOffset)
  3722         *aWordEndOffset = entry->mNodeOffset + res.mEnd - entry->mStrOffset;
  3724       break;
  3729   return NS_OK;
  3732 #ifdef DEBUG_kin
  3733 void
  3734 nsTextServicesDocument::PrintOffsetTable()
  3736   OffsetEntry *entry;
  3737   uint32_t i;
  3739   for (i = 0; i < mOffsetTable.Length(); i++)
  3741     entry = mOffsetTable[i];
  3742     printf("ENTRY %4d: %p  %c  %c  %4d  %4d  %4d\n",
  3743            i, entry->mNode,  entry->mIsValid ? 'V' : 'N',
  3744            entry->mIsInsertedText ? 'I' : 'B',
  3745            entry->mNodeOffset, entry->mStrOffset, entry->mLength);
  3748   fflush(stdout);
  3751 void
  3752 nsTextServicesDocument::PrintContentNode(nsIContent *aContent)
  3754   nsString tmpStr, str;
  3756   aContent->Tag()->ToString(tmpStr);
  3757   printf("%s", NS_LossyConvertUTF16toASCII(tmpStr).get());
  3759   if (nsIDOMNode::TEXT_NODE == aContent->NodeType())
  3761     aContent->AppendTextTo(str);
  3762     printf(":  \"%s\"", NS_LossyConvertUTF16toASCII(str).get());
  3765   printf("\n");
  3766   fflush(stdout);
  3768 #endif
  3770 NS_IMETHODIMP
  3771 nsTextServicesDocument::WillInsertNode(nsIDOMNode *aNode,
  3772                               nsIDOMNode *aParent,
  3773                               int32_t     aPosition)
  3775   return NS_OK;
  3778 NS_IMETHODIMP
  3779 nsTextServicesDocument::WillDeleteNode(nsIDOMNode *aChild)
  3781   return NS_OK;
  3784 NS_IMETHODIMP
  3785 nsTextServicesDocument::WillSplitNode(nsIDOMNode *aExistingRightNode,
  3786                              int32_t     aOffset)
  3788   return NS_OK;
  3791 NS_IMETHODIMP
  3792 nsTextServicesDocument::WillJoinNodes(nsIDOMNode  *aLeftNode,
  3793                              nsIDOMNode  *aRightNode,
  3794                              nsIDOMNode  *aParent)
  3796   return NS_OK;
  3800 // -------------------------------
  3801 // stubs for unused listen methods
  3802 // -------------------------------
  3804 NS_IMETHODIMP
  3805 nsTextServicesDocument::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition)
  3807   return NS_OK;
  3810 NS_IMETHODIMP
  3811 nsTextServicesDocument::DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition, nsresult aResult)
  3813   return NS_OK;
  3816 NS_IMETHODIMP
  3817 nsTextServicesDocument::WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString)
  3819   return NS_OK;
  3822 NS_IMETHODIMP
  3823 nsTextServicesDocument::DidInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString, nsresult aResult)
  3825   return NS_OK;
  3828 NS_IMETHODIMP
  3829 nsTextServicesDocument::WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength)
  3831   return NS_OK;
  3834 NS_IMETHODIMP
  3835 nsTextServicesDocument::DidDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength, nsresult aResult)
  3837   return NS_OK;
  3840 NS_IMETHODIMP
  3841 nsTextServicesDocument::WillDeleteSelection(nsISelection *aSelection)
  3843   return NS_OK;
  3846 NS_IMETHODIMP
  3847 nsTextServicesDocument::DidDeleteSelection(nsISelection *aSelection)
  3849   return NS_OK;

mercurial