Bumping manifests a=b2g-bump
[gecko.git] / editor / txtsvc / nsTextServicesDocument.cpp
blob25c1a3675d241aac059af39740d9b61287b5a781
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
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)
53 if (mStrOffset < 1)
54 mStrOffset = 0;
56 if (mLength < 1)
57 mLength = 0;
60 virtual ~OffsetEntry()
62 mNode = 0;
63 mNodeOffset = 0;
64 mStrOffset = 0;
65 mLength = 0;
66 mIsValid = false;
69 nsIDOMNode *mNode;
70 int32_t mNodeOffset;
71 int32_t mStrOffset;
72 int32_t mLength;
73 bool mIsInsertedText;
74 bool mIsValid;
77 #define TS_ATOM(name_, value_) nsIAtom* nsTextServicesDocument::name_ = 0;
78 #include "nsTSAtomList.h" // IWYU pragma: keep
79 #undef TS_ATOM
81 nsTextServicesDocument::nsTextServicesDocument()
83 mSelStartIndex = -1;
84 mSelStartOffset = -1;
85 mSelEndIndex = -1;
86 mSelEndOffset = -1;
88 mIteratorStatus = eIsDone;
91 nsTextServicesDocument::~nsTextServicesDocument()
93 ClearOffsetTable(&mOffsetTable);
96 #define TS_ATOM(name_, value_) NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
97 #include "nsTSAtomList.h" // IWYU pragma: keep
98 #undef TS_ATOM
100 /* static */
101 void
102 nsTextServicesDocument::RegisterAtoms()
104 static const nsStaticAtom ts_atoms[] = {
105 #define TS_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &name_),
106 #include "nsTSAtomList.h" // IWYU pragma: keep
107 #undef TS_ATOM
110 NS_RegisterStaticAtoms(ts_atoms);
113 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextServicesDocument)
114 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextServicesDocument)
116 NS_INTERFACE_MAP_BEGIN(nsTextServicesDocument)
117 NS_INTERFACE_MAP_ENTRY(nsITextServicesDocument)
118 NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
119 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITextServicesDocument)
120 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsTextServicesDocument)
121 NS_INTERFACE_MAP_END
123 NS_IMPL_CYCLE_COLLECTION(nsTextServicesDocument,
124 mDOMDocument,
125 mSelCon,
126 mIterator,
127 mPrevTextBlock,
128 mNextTextBlock,
129 mExtent,
130 mTxtSvcFilter)
132 NS_IMETHODIMP
133 nsTextServicesDocument::InitWithEditor(nsIEditor *aEditor)
135 nsresult result = NS_OK;
136 nsCOMPtr<nsISelectionController> selCon;
137 nsCOMPtr<nsIDOMDocument> doc;
139 NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
141 LOCK_DOC(this);
143 // Check to see if we already have an mSelCon. If we do, it
144 // better be the same one the editor uses!
146 result = aEditor->GetSelectionController(getter_AddRefs(selCon));
148 if (NS_FAILED(result))
150 UNLOCK_DOC(this);
151 return result;
154 if (!selCon || (mSelCon && selCon != mSelCon))
156 UNLOCK_DOC(this);
157 return NS_ERROR_FAILURE;
160 if (!mSelCon)
161 mSelCon = selCon;
163 // Check to see if we already have an mDOMDocument. If we do, it
164 // better be the same one the editor uses!
166 result = aEditor->GetDocument(getter_AddRefs(doc));
168 if (NS_FAILED(result))
170 UNLOCK_DOC(this);
171 return result;
174 if (!doc || (mDOMDocument && doc != mDOMDocument))
176 UNLOCK_DOC(this);
177 return NS_ERROR_FAILURE;
180 if (!mDOMDocument)
182 mDOMDocument = doc;
184 result = CreateDocumentContentIterator(getter_AddRefs(mIterator));
186 if (NS_FAILED(result))
188 UNLOCK_DOC(this);
189 return result;
192 mIteratorStatus = nsTextServicesDocument::eIsDone;
194 result = FirstBlock();
196 if (NS_FAILED(result))
198 UNLOCK_DOC(this);
199 return result;
203 mEditor = do_GetWeakReference(aEditor);
205 result = aEditor->AddEditActionListener(this);
207 UNLOCK_DOC(this);
209 return result;
212 NS_IMETHODIMP
213 nsTextServicesDocument::GetDocument(nsIDOMDocument **aDoc)
215 NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
217 *aDoc = nullptr; // init out param
218 NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_NOT_INITIALIZED);
220 *aDoc = mDOMDocument;
221 NS_ADDREF(*aDoc);
223 return NS_OK;
226 NS_IMETHODIMP
227 nsTextServicesDocument::SetExtent(nsIDOMRange* aDOMRange)
229 NS_ENSURE_ARG_POINTER(aDOMRange);
230 NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
232 LOCK_DOC(this);
234 // We need to store a copy of aDOMRange since we don't
235 // know where it came from.
237 nsresult result = aDOMRange->CloneRange(getter_AddRefs(mExtent));
239 if (NS_FAILED(result))
241 UNLOCK_DOC(this);
242 return result;
245 // Create a new iterator based on our new extent range.
247 result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
249 if (NS_FAILED(result))
251 UNLOCK_DOC(this);
252 return result;
255 // Now position the iterator at the start of the first block
256 // in the range.
258 mIteratorStatus = nsTextServicesDocument::eIsDone;
260 result = FirstBlock();
262 UNLOCK_DOC(this);
264 return result;
267 NS_IMETHODIMP
268 nsTextServicesDocument::ExpandRangeToWordBoundaries(nsIDOMRange *aRange)
270 NS_ENSURE_ARG_POINTER(aRange);
272 // Get the end points of the range.
274 nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
275 int32_t rngStartOffset, rngEndOffset;
277 nsresult result = GetRangeEndPoints(aRange,
278 getter_AddRefs(rngStartNode),
279 &rngStartOffset,
280 getter_AddRefs(rngEndNode),
281 &rngEndOffset);
283 NS_ENSURE_SUCCESS(result, result);
285 // Create a content iterator based on the range.
287 nsCOMPtr<nsIContentIterator> iter;
288 result = CreateContentIterator(aRange, getter_AddRefs(iter));
290 NS_ENSURE_SUCCESS(result, result);
292 // Find the first text node in the range.
294 TSDIteratorStatus iterStatus;
296 result = FirstTextNode(iter, &iterStatus);
297 NS_ENSURE_SUCCESS(result, result);
299 if (iterStatus == nsTextServicesDocument::eIsDone)
301 // No text was found so there's no adjustment necessary!
302 return NS_OK;
305 nsINode *firstText = iter->GetCurrentNode();
306 NS_ENSURE_TRUE(firstText, NS_ERROR_FAILURE);
308 // Find the last text node in the range.
310 result = LastTextNode(iter, &iterStatus);
311 NS_ENSURE_SUCCESS(result, result);
313 if (iterStatus == nsTextServicesDocument::eIsDone)
315 // We should never get here because a first text block
316 // was found above.
317 NS_ASSERTION(false, "Found a first without a last!");
318 return NS_ERROR_FAILURE;
321 nsINode *lastText = iter->GetCurrentNode();
322 NS_ENSURE_TRUE(lastText, NS_ERROR_FAILURE);
324 // Now make sure our end points are in terms of text nodes in the range!
326 nsCOMPtr<nsIDOMNode> firstTextNode = do_QueryInterface(firstText);
327 NS_ENSURE_TRUE(firstTextNode, NS_ERROR_FAILURE);
329 if (rngStartNode != firstTextNode)
331 // The range includes the start of the first text node!
332 rngStartNode = firstTextNode;
333 rngStartOffset = 0;
336 nsCOMPtr<nsIDOMNode> lastTextNode = do_QueryInterface(lastText);
337 NS_ENSURE_TRUE(lastTextNode, NS_ERROR_FAILURE);
339 if (rngEndNode != lastTextNode)
341 // The range includes the end of the last text node!
342 rngEndNode = lastTextNode;
343 nsAutoString str;
344 result = lastTextNode->GetNodeValue(str);
345 rngEndOffset = str.Length();
348 // Create a doc iterator so that we can scan beyond
349 // the bounds of the extent range.
351 nsCOMPtr<nsIContentIterator> docIter;
352 result = CreateDocumentContentIterator(getter_AddRefs(docIter));
353 NS_ENSURE_SUCCESS(result, result);
355 // Grab all the text in the block containing our
356 // first text node.
358 result = docIter->PositionAt(firstText);
359 NS_ENSURE_SUCCESS(result, result);
361 iterStatus = nsTextServicesDocument::eValid;
363 nsTArray<OffsetEntry*> offsetTable;
364 nsAutoString blockStr;
366 result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
367 nullptr, &blockStr);
368 if (NS_FAILED(result))
370 ClearOffsetTable(&offsetTable);
371 return result;
374 nsCOMPtr<nsIDOMNode> wordStartNode, wordEndNode;
375 int32_t wordStartOffset, wordEndOffset;
377 result = FindWordBounds(&offsetTable, &blockStr,
378 rngStartNode, rngStartOffset,
379 getter_AddRefs(wordStartNode), &wordStartOffset,
380 getter_AddRefs(wordEndNode), &wordEndOffset);
382 ClearOffsetTable(&offsetTable);
384 NS_ENSURE_SUCCESS(result, result);
386 rngStartNode = wordStartNode;
387 rngStartOffset = wordStartOffset;
389 // Grab all the text in the block containing our
390 // last text node.
392 result = docIter->PositionAt(lastText);
393 NS_ENSURE_SUCCESS(result, result);
395 iterStatus = nsTextServicesDocument::eValid;
397 result = CreateOffsetTable(&offsetTable, docIter, &iterStatus,
398 nullptr, &blockStr);
399 if (NS_FAILED(result))
401 ClearOffsetTable(&offsetTable);
402 return result;
405 result = FindWordBounds(&offsetTable, &blockStr,
406 rngEndNode, rngEndOffset,
407 getter_AddRefs(wordStartNode), &wordStartOffset,
408 getter_AddRefs(wordEndNode), &wordEndOffset);
410 ClearOffsetTable(&offsetTable);
412 NS_ENSURE_SUCCESS(result, result);
414 // To prevent expanding the range too much, we only change
415 // rngEndNode and rngEndOffset if it isn't already at the start of the
416 // word and isn't equivalent to rngStartNode and rngStartOffset.
418 if (rngEndNode != wordStartNode || rngEndOffset != wordStartOffset ||
419 (rngEndNode == rngStartNode && rngEndOffset == rngStartOffset))
421 rngEndNode = wordEndNode;
422 rngEndOffset = wordEndOffset;
425 // Now adjust the range so that it uses our new
426 // end points.
428 result = aRange->SetEnd(rngEndNode, rngEndOffset);
429 NS_ENSURE_SUCCESS(result, result);
431 return aRange->SetStart(rngStartNode, rngStartOffset);
434 NS_IMETHODIMP
435 nsTextServicesDocument::SetFilter(nsITextServicesFilter *aFilter)
437 // Hang on to the filter so we can set it into the filtered iterator.
438 mTxtSvcFilter = aFilter;
440 return NS_OK;
443 NS_IMETHODIMP
444 nsTextServicesDocument::GetCurrentTextBlock(nsString *aStr)
446 nsresult result;
448 NS_ENSURE_TRUE(aStr, NS_ERROR_NULL_POINTER);
450 aStr->Truncate();
452 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
454 LOCK_DOC(this);
456 result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
457 mExtent, aStr);
459 UNLOCK_DOC(this);
461 return result;
464 NS_IMETHODIMP
465 nsTextServicesDocument::FirstBlock()
467 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
469 LOCK_DOC(this);
471 nsresult result = FirstTextNode(mIterator, &mIteratorStatus);
473 if (NS_FAILED(result))
475 UNLOCK_DOC(this);
476 return result;
479 // Keep track of prev and next blocks, just in case
480 // the text service blows away the current block.
482 if (mIteratorStatus == nsTextServicesDocument::eValid)
484 mPrevTextBlock = nullptr;
485 result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
487 else
489 // There's no text block in the document!
491 mPrevTextBlock = nullptr;
492 mNextTextBlock = nullptr;
495 UNLOCK_DOC(this);
497 return result;
500 NS_IMETHODIMP
501 nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus *aSelStatus,
502 int32_t *aSelOffset,
503 int32_t *aSelLength)
505 nsresult result = NS_OK;
507 NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
509 LOCK_DOC(this);
511 mIteratorStatus = nsTextServicesDocument::eIsDone;
513 *aSelStatus = nsITextServicesDocument::eBlockNotFound;
514 *aSelOffset = *aSelLength = -1;
516 if (!mSelCon || !mIterator)
518 UNLOCK_DOC(this);
519 return NS_ERROR_FAILURE;
522 nsCOMPtr<nsISelection> selection;
523 bool isCollapsed = false;
525 result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
527 if (NS_FAILED(result))
529 UNLOCK_DOC(this);
530 return result;
533 result = selection->GetIsCollapsed(&isCollapsed);
535 if (NS_FAILED(result))
537 UNLOCK_DOC(this);
538 return result;
541 nsCOMPtr<nsIContentIterator> iter;
542 nsCOMPtr<nsIDOMRange> range;
543 nsCOMPtr<nsIDOMNode> parent;
544 int32_t i, rangeCount, offset;
546 if (isCollapsed)
548 // We have a caret. Check if the caret is in a text node.
549 // If it is, make the text node's block the current block.
550 // If the caret isn't in a text node, search forwards in
551 // the document, till we find a text node.
553 result = selection->GetRangeAt(0, getter_AddRefs(range));
555 if (NS_FAILED(result))
557 UNLOCK_DOC(this);
558 return result;
561 if (!range)
563 UNLOCK_DOC(this);
564 return NS_ERROR_FAILURE;
567 result = range->GetStartContainer(getter_AddRefs(parent));
569 if (NS_FAILED(result))
571 UNLOCK_DOC(this);
572 return result;
575 if (!parent)
577 UNLOCK_DOC(this);
578 return NS_ERROR_FAILURE;
581 result = range->GetStartOffset(&offset);
583 if (NS_FAILED(result))
585 UNLOCK_DOC(this);
586 return result;
589 if (IsTextNode(parent))
591 // The caret is in a text node. Find the beginning
592 // of the text block containing this text node and
593 // return.
595 nsCOMPtr<nsIContent> content(do_QueryInterface(parent));
597 if (!content)
599 UNLOCK_DOC(this);
600 return NS_ERROR_FAILURE;
603 result = mIterator->PositionAt(content);
605 if (NS_FAILED(result))
607 UNLOCK_DOC(this);
608 return result;
611 result = FirstTextNodeInCurrentBlock(mIterator);
613 if (NS_FAILED(result))
615 UNLOCK_DOC(this);
616 return result;
619 mIteratorStatus = nsTextServicesDocument::eValid;
621 result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
622 mExtent, nullptr);
624 if (NS_FAILED(result))
626 UNLOCK_DOC(this);
627 return result;
630 result = GetSelection(aSelStatus, aSelOffset, aSelLength);
632 if (NS_FAILED(result))
634 UNLOCK_DOC(this);
635 return result;
638 if (*aSelStatus == nsITextServicesDocument::eBlockContains)
639 result = SetSelectionInternal(*aSelOffset, *aSelLength, false);
641 else
643 // The caret isn't in a text node. Create an iterator
644 // based on a range that extends from the current caret
645 // position to the end of the document, then walk forwards
646 // till you find a text node, then find the beginning of it's block.
648 result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, getter_AddRefs(range));
650 if (NS_FAILED(result))
652 UNLOCK_DOC(this);
653 return result;
656 result = range->GetCollapsed(&isCollapsed);
658 if (NS_FAILED(result))
660 UNLOCK_DOC(this);
661 return result;
664 if (isCollapsed)
666 // If we get here, the range is collapsed because there is nothing after
667 // the caret! Just return NS_OK;
669 UNLOCK_DOC(this);
670 return NS_OK;
673 result = CreateContentIterator(range, getter_AddRefs(iter));
675 if (NS_FAILED(result))
677 UNLOCK_DOC(this);
678 return result;
681 iter->First();
683 nsCOMPtr<nsIContent> content;
684 while (!iter->IsDone())
686 content = do_QueryInterface(iter->GetCurrentNode());
688 if (IsTextNode(content))
689 break;
691 content = nullptr;
693 iter->Next();
696 if (!content)
698 UNLOCK_DOC(this);
699 return NS_OK;
702 result = mIterator->PositionAt(content);
704 if (NS_FAILED(result))
706 UNLOCK_DOC(this);
707 return result;
710 result = FirstTextNodeInCurrentBlock(mIterator);
712 if (NS_FAILED(result))
714 UNLOCK_DOC(this);
715 return result;
718 mIteratorStatus = nsTextServicesDocument::eValid;
720 result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
721 mExtent, nullptr);
723 if (NS_FAILED(result))
725 UNLOCK_DOC(this);
726 return result;
729 result = GetSelection(aSelStatus, aSelOffset, aSelLength);
731 if (NS_FAILED(result))
733 UNLOCK_DOC(this);
734 return result;
738 UNLOCK_DOC(this);
740 return result;
743 // If we get here, we have an uncollapsed selection!
744 // Look backwards through each range in the selection till you
745 // find the first text node. If you find one, find the
746 // beginning of its text block, and make it the current
747 // block.
749 result = selection->GetRangeCount(&rangeCount);
751 if (NS_FAILED(result))
753 UNLOCK_DOC(this);
754 return result;
757 NS_ASSERTION(rangeCount > 0, "Unexpected range count!");
759 if (rangeCount <= 0)
761 UNLOCK_DOC(this);
762 return NS_OK;
765 // XXX: We may need to add some code here to make sure
766 // the ranges are sorted in document appearance order!
768 for (i = rangeCount - 1; i >= 0; i--)
770 // Get the i'th range from the selection.
772 result = selection->GetRangeAt(i, getter_AddRefs(range));
774 if (NS_FAILED(result))
776 UNLOCK_DOC(this);
777 return result;
780 // Create an iterator for the range.
782 result = CreateContentIterator(range, getter_AddRefs(iter));
784 if (NS_FAILED(result))
786 UNLOCK_DOC(this);
787 return result;
790 iter->Last();
792 // Now walk through the range till we find a text node.
794 while (!iter->IsDone())
796 if (iter->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
797 // We found a text node, so position the document's
798 // iterator at the beginning of the block, then get
799 // the selection in terms of the string offset.
800 nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
802 result = mIterator->PositionAt(content);
804 if (NS_FAILED(result))
806 UNLOCK_DOC(this);
807 return result;
810 result = FirstTextNodeInCurrentBlock(mIterator);
812 if (NS_FAILED(result))
814 UNLOCK_DOC(this);
815 return result;
818 mIteratorStatus = nsTextServicesDocument::eValid;
820 result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
821 mExtent, nullptr);
823 if (NS_FAILED(result))
825 UNLOCK_DOC(this);
826 return result;
829 result = GetSelection(aSelStatus, aSelOffset, aSelLength);
831 UNLOCK_DOC(this);
833 return result;
837 iter->Prev();
841 // If we get here, we didn't find any text node in the selection!
842 // Create a range that extends from the end of the selection,
843 // to the end of the document, then iterate forwards through
844 // it till you find a text node!
846 result = selection->GetRangeAt(rangeCount - 1, getter_AddRefs(range));
848 if (NS_FAILED(result))
850 UNLOCK_DOC(this);
851 return result;
854 if (!range)
856 UNLOCK_DOC(this);
857 return NS_ERROR_FAILURE;
860 result = range->GetEndContainer(getter_AddRefs(parent));
862 if (NS_FAILED(result))
864 UNLOCK_DOC(this);
865 return result;
868 if (!parent)
870 UNLOCK_DOC(this);
871 return NS_ERROR_FAILURE;
874 result = range->GetEndOffset(&offset);
876 if (NS_FAILED(result))
878 UNLOCK_DOC(this);
879 return result;
882 result = CreateDocumentContentRootToNodeOffsetRange(parent, offset, false, getter_AddRefs(range));
884 if (NS_FAILED(result))
886 UNLOCK_DOC(this);
887 return result;
890 result = range->GetCollapsed(&isCollapsed);
892 if (NS_FAILED(result))
894 UNLOCK_DOC(this);
895 return result;
898 if (isCollapsed)
900 // If we get here, the range is collapsed because there is nothing after
901 // the current selection! Just return NS_OK;
903 UNLOCK_DOC(this);
904 return NS_OK;
907 result = CreateContentIterator(range, getter_AddRefs(iter));
909 if (NS_FAILED(result))
911 UNLOCK_DOC(this);
912 return result;
915 iter->First();
917 while (!iter->IsDone())
919 if (iter->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
920 // We found a text node! Adjust the document's iterator to point
921 // to the beginning of its text block, then get the current selection.
922 nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->AsContent();
924 result = mIterator->PositionAt(content);
926 if (NS_FAILED(result))
928 UNLOCK_DOC(this);
929 return result;
932 result = FirstTextNodeInCurrentBlock(mIterator);
934 if (NS_FAILED(result))
936 UNLOCK_DOC(this);
937 return result;
941 mIteratorStatus = nsTextServicesDocument::eValid;
943 result = CreateOffsetTable(&mOffsetTable, mIterator, &mIteratorStatus,
944 mExtent, nullptr);
946 if (NS_FAILED(result))
948 UNLOCK_DOC(this);
949 return result;
952 result = GetSelection(aSelStatus, aSelOffset, aSelLength);
954 UNLOCK_DOC(this);
956 return result;
959 iter->Next();
962 // If we get here, we didn't find any block before or inside
963 // the selection! Just return OK.
965 UNLOCK_DOC(this);
967 return NS_OK;
970 NS_IMETHODIMP
971 nsTextServicesDocument::PrevBlock()
973 nsresult result = NS_OK;
975 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
977 LOCK_DOC(this);
979 if (mIteratorStatus == nsTextServicesDocument::eIsDone)
980 return NS_OK;
982 switch (mIteratorStatus)
984 case nsTextServicesDocument::eValid:
985 case nsTextServicesDocument::eNext:
987 result = FirstTextNodeInPrevBlock(mIterator);
989 if (NS_FAILED(result))
991 mIteratorStatus = nsTextServicesDocument::eIsDone;
992 UNLOCK_DOC(this);
993 return result;
996 if (mIterator->IsDone())
998 mIteratorStatus = nsTextServicesDocument::eIsDone;
999 UNLOCK_DOC(this);
1000 return NS_OK;
1003 mIteratorStatus = nsTextServicesDocument::eValid;
1004 break;
1006 case nsTextServicesDocument::ePrev:
1008 // The iterator already points to the previous
1009 // block, so don't do anything.
1011 mIteratorStatus = nsTextServicesDocument::eValid;
1012 break;
1014 default:
1016 mIteratorStatus = nsTextServicesDocument::eIsDone;
1017 break;
1020 // Keep track of prev and next blocks, just in case
1021 // the text service blows away the current block.
1023 if (mIteratorStatus == nsTextServicesDocument::eValid)
1025 result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
1026 result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
1028 else
1030 // We must be done!
1032 mPrevTextBlock = nullptr;
1033 mNextTextBlock = nullptr;
1036 UNLOCK_DOC(this);
1038 return result;
1041 NS_IMETHODIMP
1042 nsTextServicesDocument::NextBlock()
1044 nsresult result = NS_OK;
1046 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
1048 LOCK_DOC(this);
1050 if (mIteratorStatus == nsTextServicesDocument::eIsDone)
1051 return NS_OK;
1053 switch (mIteratorStatus)
1055 case nsTextServicesDocument::eValid:
1057 // Advance the iterator to the next text block.
1059 result = FirstTextNodeInNextBlock(mIterator);
1061 if (NS_FAILED(result))
1063 mIteratorStatus = nsTextServicesDocument::eIsDone;
1064 UNLOCK_DOC(this);
1065 return result;
1068 if (mIterator->IsDone())
1070 mIteratorStatus = nsTextServicesDocument::eIsDone;
1071 UNLOCK_DOC(this);
1072 return NS_OK;
1075 mIteratorStatus = nsTextServicesDocument::eValid;
1076 break;
1078 case nsTextServicesDocument::eNext:
1080 // The iterator already points to the next block,
1081 // so don't do anything to it!
1083 mIteratorStatus = nsTextServicesDocument::eValid;
1084 break;
1086 case nsTextServicesDocument::ePrev:
1088 // If the iterator is pointing to the previous block,
1089 // we know that there is no next text block! Just
1090 // fall through to the default case!
1092 default:
1094 mIteratorStatus = nsTextServicesDocument::eIsDone;
1095 break;
1098 // Keep track of prev and next blocks, just in case
1099 // the text service blows away the current block.
1101 if (mIteratorStatus == nsTextServicesDocument::eValid)
1103 result = GetFirstTextNodeInPrevBlock(getter_AddRefs(mPrevTextBlock));
1104 result = GetFirstTextNodeInNextBlock(getter_AddRefs(mNextTextBlock));
1106 else
1108 // We must be done.
1110 mPrevTextBlock = nullptr;
1111 mNextTextBlock = nullptr;
1115 UNLOCK_DOC(this);
1117 return result;
1120 NS_IMETHODIMP
1121 nsTextServicesDocument::IsDone(bool *aIsDone)
1123 NS_ENSURE_TRUE(aIsDone, NS_ERROR_NULL_POINTER);
1125 *aIsDone = false;
1127 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
1129 LOCK_DOC(this);
1131 *aIsDone = (mIteratorStatus == nsTextServicesDocument::eIsDone) ? true : false;
1133 UNLOCK_DOC(this);
1135 return NS_OK;
1138 NS_IMETHODIMP
1139 nsTextServicesDocument::SetSelection(int32_t aOffset, int32_t aLength)
1141 nsresult result;
1143 NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
1145 LOCK_DOC(this);
1147 result = SetSelectionInternal(aOffset, aLength, true);
1149 UNLOCK_DOC(this);
1151 //**** KDEBUG ****
1152 // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1153 //**** KDEBUG ****
1155 return result;
1158 NS_IMETHODIMP
1159 nsTextServicesDocument::ScrollSelectionIntoView()
1161 nsresult result;
1163 NS_ENSURE_TRUE(mSelCon, NS_ERROR_FAILURE);
1165 LOCK_DOC(this);
1167 // After ScrollSelectionIntoView(), the pending notifications might be flushed
1168 // and PresShell/PresContext/Frames may be dead. See bug 418470.
1169 result = mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION,
1170 nsISelectionController::SCROLL_SYNCHRONOUS);
1172 UNLOCK_DOC(this);
1174 return result;
1177 NS_IMETHODIMP
1178 nsTextServicesDocument::DeleteSelection()
1180 nsresult result = NS_OK;
1182 // We don't allow deletion during a collapsed selection!
1183 nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
1184 NS_ASSERTION(editor, "DeleteSelection called without an editor present!");
1185 NS_ASSERTION(SelectionIsValid(), "DeleteSelection called without a valid selection!");
1187 if (!editor || !SelectionIsValid())
1188 return NS_ERROR_FAILURE;
1190 if (SelectionIsCollapsed())
1191 return NS_OK;
1193 LOCK_DOC(this);
1195 //**** KDEBUG ****
1196 // printf("\n---- Before Delete\n");
1197 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1198 // PrintOffsetTable();
1199 //**** KDEBUG ****
1201 // If we have an mExtent, save off its current set of
1202 // end points so we can compare them against mExtent's
1203 // set after the deletion of the content.
1205 nsCOMPtr<nsIDOMNode> origStartNode, origEndNode;
1206 int32_t origStartOffset = 0, origEndOffset = 0;
1208 if (mExtent)
1210 result = GetRangeEndPoints(mExtent,
1211 getter_AddRefs(origStartNode), &origStartOffset,
1212 getter_AddRefs(origEndNode), &origEndOffset);
1214 if (NS_FAILED(result))
1216 UNLOCK_DOC(this);
1217 return result;
1221 int32_t i, selLength;
1222 OffsetEntry *entry, *newEntry;
1224 for (i = mSelStartIndex; i <= mSelEndIndex; i++)
1226 entry = mOffsetTable[i];
1228 if (i == mSelStartIndex)
1230 // Calculate the length of the selection. Note that the
1231 // selection length can be zero if the start of the selection
1232 // is at the very end of a text node entry.
1234 if (entry->mIsInsertedText)
1236 // Inserted text offset entries have no width when
1237 // talking in terms of string offsets! If the beginning
1238 // of the selection is in an inserted text offset entry,
1239 // the caret is always at the end of the entry!
1241 selLength = 0;
1243 else
1244 selLength = entry->mLength - (mSelStartOffset - entry->mStrOffset);
1246 if (selLength > 0 && mSelStartOffset > entry->mStrOffset)
1248 // Selection doesn't start at the beginning of the
1249 // text node entry. We need to split this entry into
1250 // two pieces, the piece before the selection, and
1251 // the piece inside the selection.
1253 result = SplitOffsetEntry(i, selLength);
1255 if (NS_FAILED(result))
1257 UNLOCK_DOC(this);
1258 return result;
1261 // Adjust selection indexes to account for new entry:
1263 ++mSelStartIndex;
1264 ++mSelEndIndex;
1265 ++i;
1267 entry = mOffsetTable[i];
1271 if (selLength > 0 && mSelStartIndex < mSelEndIndex)
1273 // The entire entry is contained in the selection. Mark the
1274 // entry invalid.
1276 entry->mIsValid = false;
1280 //**** KDEBUG ****
1281 // printf("\n---- Middle Delete\n");
1282 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1283 // PrintOffsetTable();
1284 //**** KDEBUG ****
1286 if (i == mSelEndIndex)
1288 if (entry->mIsInsertedText)
1290 // Inserted text offset entries have no width when
1291 // talking in terms of string offsets! If the end
1292 // of the selection is in an inserted text offset entry,
1293 // the selection includes the entire entry!
1295 entry->mIsValid = false;
1297 else
1299 // Calculate the length of the selection. Note that the
1300 // selection length can be zero if the end of the selection
1301 // is at the very beginning of a text node entry.
1303 selLength = mSelEndOffset - entry->mStrOffset;
1305 if (selLength > 0 && mSelEndOffset < entry->mStrOffset + entry->mLength)
1307 // mStrOffset is guaranteed to be inside the selection, even
1308 // when mSelStartIndex == mSelEndIndex.
1310 result = SplitOffsetEntry(i, entry->mLength - selLength);
1312 if (NS_FAILED(result))
1314 UNLOCK_DOC(this);
1315 return result;
1318 // Update the entry fields:
1320 newEntry = mOffsetTable[i+1];
1321 newEntry->mNodeOffset = entry->mNodeOffset;
1325 if (selLength > 0 && mSelEndOffset == entry->mStrOffset + entry->mLength)
1327 // The entire entry is contained in the selection. Mark the
1328 // entry invalid.
1330 entry->mIsValid = false;
1335 if (i != mSelStartIndex && i != mSelEndIndex)
1337 // The entire entry is contained in the selection. Mark the
1338 // entry invalid.
1340 entry->mIsValid = false;
1344 // Make sure mIterator always points to something valid!
1346 AdjustContentIterator();
1348 // Now delete the actual content!
1350 result = editor->DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip);
1352 if (NS_FAILED(result))
1354 UNLOCK_DOC(this);
1355 return result;
1358 // Now that we've actually deleted the selected content,
1359 // check to see if our mExtent has changed, if so, then
1360 // we have to create a new content iterator!
1362 if (origStartNode && origEndNode)
1364 nsCOMPtr<nsIDOMNode> curStartNode, curEndNode;
1365 int32_t curStartOffset = 0, curEndOffset = 0;
1367 result = GetRangeEndPoints(mExtent,
1368 getter_AddRefs(curStartNode), &curStartOffset,
1369 getter_AddRefs(curEndNode), &curEndOffset);
1371 if (NS_FAILED(result))
1373 UNLOCK_DOC(this);
1374 return result;
1377 if (origStartNode != curStartNode || origEndNode != curEndNode)
1379 // The range has changed, so we need to create a new content
1380 // iterator based on the new range.
1382 nsCOMPtr<nsIContent> curContent;
1384 if (mIteratorStatus != nsTextServicesDocument::eIsDone) {
1385 // The old iterator is still pointing to something valid,
1386 // so get its current node so we can restore it after we
1387 // create the new iterator!
1389 curContent = mIterator->GetCurrentNode()
1390 ? mIterator->GetCurrentNode()->AsContent()
1391 : nullptr;
1394 // Create the new iterator.
1396 result = CreateContentIterator(mExtent, getter_AddRefs(mIterator));
1398 if (NS_FAILED(result))
1400 UNLOCK_DOC(this);
1401 return result;
1404 // Now make the new iterator point to the content node
1405 // the old one was pointing at.
1407 if (curContent)
1409 result = mIterator->PositionAt(curContent);
1411 if (NS_FAILED(result))
1412 mIteratorStatus = eIsDone;
1413 else
1414 mIteratorStatus = eValid;
1419 entry = 0;
1421 // Move the caret to the end of the first valid entry.
1422 // Start with mSelStartIndex since it may still be valid.
1424 for (i = mSelStartIndex; !entry && i >= 0; i--)
1426 entry = mOffsetTable[i];
1428 if (!entry->mIsValid)
1429 entry = 0;
1430 else
1432 mSelStartIndex = mSelEndIndex = i;
1433 mSelStartOffset = mSelEndOffset = entry->mStrOffset + entry->mLength;
1437 // If we still don't have a valid entry, move the caret
1438 // to the next valid entry after the selection:
1440 for (i = mSelEndIndex; !entry && i < int32_t(mOffsetTable.Length()); i++)
1442 entry = mOffsetTable[i];
1444 if (!entry->mIsValid)
1445 entry = 0;
1446 else
1448 mSelStartIndex = mSelEndIndex = i;
1449 mSelStartOffset = mSelEndOffset = entry->mStrOffset;
1453 if (entry)
1454 result = SetSelection(mSelStartOffset, 0);
1455 else
1457 // Uuughh we have no valid offset entry to place our
1458 // caret ... just mark the selection invalid.
1460 mSelStartIndex = mSelEndIndex = -1;
1461 mSelStartOffset = mSelEndOffset = -1;
1464 // Now remove any invalid entries from the offset table.
1466 result = RemoveInvalidOffsetEntries();
1468 //**** KDEBUG ****
1469 // printf("\n---- After Delete\n");
1470 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1471 // PrintOffsetTable();
1472 //**** KDEBUG ****
1474 UNLOCK_DOC(this);
1476 return result;
1479 NS_IMETHODIMP
1480 nsTextServicesDocument::InsertText(const nsString *aText)
1482 nsresult result = NS_OK;
1484 nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
1485 NS_ASSERTION(editor, "InsertText called without an editor present!");
1487 if (!editor || !SelectionIsValid())
1488 return NS_ERROR_FAILURE;
1490 NS_ENSURE_TRUE(aText, NS_ERROR_NULL_POINTER);
1492 // If the selection is not collapsed, we need to save
1493 // off the selection offsets so we can restore the
1494 // selection and delete the selected content after we've
1495 // inserted the new text. This is necessary to try and
1496 // retain as much of the original style of the content
1497 // being deleted.
1499 bool collapsedSelection = SelectionIsCollapsed();
1500 int32_t savedSelOffset = mSelStartOffset;
1501 int32_t savedSelLength = mSelEndOffset - mSelStartOffset;
1503 if (!collapsedSelection)
1505 // Collapse to the start of the current selection
1506 // for the insert!
1508 result = SetSelection(mSelStartOffset, 0);
1510 NS_ENSURE_SUCCESS(result, result);
1514 LOCK_DOC(this);
1516 result = editor->BeginTransaction();
1518 if (NS_FAILED(result))
1520 UNLOCK_DOC(this);
1521 return result;
1524 nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(editor, &result));
1525 if (textEditor)
1526 result = textEditor->InsertText(*aText);
1528 if (NS_FAILED(result))
1530 editor->EndTransaction();
1531 UNLOCK_DOC(this);
1532 return result;
1535 //**** KDEBUG ****
1536 // printf("\n---- Before Insert\n");
1537 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1538 // PrintOffsetTable();
1539 //**** KDEBUG ****
1541 int32_t strLength = aText->Length();
1542 uint32_t i;
1544 nsCOMPtr<nsISelection> selection;
1545 OffsetEntry *itEntry;
1546 OffsetEntry *entry = mOffsetTable[mSelStartIndex];
1547 void *node = entry->mNode;
1549 NS_ASSERTION((entry->mIsValid), "Invalid insertion point!");
1551 if (entry->mStrOffset == mSelStartOffset)
1553 if (entry->mIsInsertedText)
1555 // If the caret is in an inserted text offset entry,
1556 // we simply insert the text at the end of the entry.
1558 entry->mLength += strLength;
1560 else
1562 // Insert an inserted text offset entry before the current
1563 // entry!
1565 itEntry = new OffsetEntry(entry->mNode, entry->mStrOffset, strLength);
1567 if (!itEntry)
1569 editor->EndTransaction();
1570 UNLOCK_DOC(this);
1571 return NS_ERROR_OUT_OF_MEMORY;
1574 itEntry->mIsInsertedText = true;
1575 itEntry->mNodeOffset = entry->mNodeOffset;
1577 if (!mOffsetTable.InsertElementAt(mSelStartIndex, itEntry))
1579 editor->EndTransaction();
1580 UNLOCK_DOC(this);
1581 return NS_ERROR_FAILURE;
1585 else if ((entry->mStrOffset + entry->mLength) == mSelStartOffset)
1587 // We are inserting text at the end of the current offset entry.
1588 // Look at the next valid entry in the table. If it's an inserted
1589 // text entry, add to its length and adjust its node offset. If
1590 // it isn't, add a new inserted text entry.
1592 i = mSelStartIndex + 1;
1593 itEntry = 0;
1595 if (mOffsetTable.Length() > i)
1597 itEntry = mOffsetTable[i];
1599 if (!itEntry)
1601 editor->EndTransaction();
1602 UNLOCK_DOC(this);
1603 return NS_ERROR_FAILURE;
1606 // Check if the entry is a match. If it isn't, set
1607 // iEntry to zero.
1609 if (!itEntry->mIsInsertedText || itEntry->mStrOffset != mSelStartOffset)
1610 itEntry = 0;
1613 if (!itEntry)
1615 // We didn't find an inserted text offset entry, so
1616 // create one.
1618 itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, 0);
1620 if (!itEntry)
1622 editor->EndTransaction();
1623 UNLOCK_DOC(this);
1624 return NS_ERROR_OUT_OF_MEMORY;
1627 itEntry->mNodeOffset = entry->mNodeOffset + entry->mLength;
1628 itEntry->mIsInsertedText = true;
1630 if (!mOffsetTable.InsertElementAt(i, itEntry))
1632 delete itEntry;
1633 return NS_ERROR_FAILURE;
1637 // We have a valid inserted text offset entry. Update its
1638 // length, adjust the selection indexes, and make sure the
1639 // caret is properly placed!
1641 itEntry->mLength += strLength;
1643 mSelStartIndex = mSelEndIndex = i;
1645 result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1647 if (NS_FAILED(result))
1649 editor->EndTransaction();
1650 UNLOCK_DOC(this);
1651 return result;
1654 result = selection->Collapse(itEntry->mNode, itEntry->mNodeOffset + itEntry->mLength);
1656 if (NS_FAILED(result))
1658 editor->EndTransaction();
1659 UNLOCK_DOC(this);
1660 return result;
1663 else if ((entry->mStrOffset + entry->mLength) > mSelStartOffset)
1665 // We are inserting text into the middle of the current offset entry.
1666 // split the current entry into two parts, then insert an inserted text
1667 // entry between them!
1669 i = entry->mLength - (mSelStartOffset - entry->mStrOffset);
1671 result = SplitOffsetEntry(mSelStartIndex, i);
1673 if (NS_FAILED(result))
1675 editor->EndTransaction();
1676 UNLOCK_DOC(this);
1677 return result;
1680 itEntry = new OffsetEntry(entry->mNode, mSelStartOffset, strLength);
1682 if (!itEntry)
1684 editor->EndTransaction();
1685 UNLOCK_DOC(this);
1686 return NS_ERROR_OUT_OF_MEMORY;
1689 itEntry->mIsInsertedText = true;
1690 itEntry->mNodeOffset = entry->mNodeOffset + entry->mLength;
1692 if (!mOffsetTable.InsertElementAt(mSelStartIndex + 1, itEntry))
1694 editor->EndTransaction();
1695 UNLOCK_DOC(this);
1696 return NS_ERROR_FAILURE;
1699 mSelEndIndex = ++mSelStartIndex;
1702 // We've just finished inserting an inserted text offset entry.
1703 // update all entries with the same mNode pointer that follow
1704 // it in the table!
1706 for (i = mSelStartIndex + 1; i < mOffsetTable.Length(); i++)
1708 entry = mOffsetTable[i];
1710 if (entry->mNode == node)
1712 if (entry->mIsValid)
1713 entry->mNodeOffset += strLength;
1715 else
1716 break;
1719 //**** KDEBUG ****
1720 // printf("\n---- After Insert\n");
1721 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1722 // PrintOffsetTable();
1723 //**** KDEBUG ****
1725 if (!collapsedSelection)
1727 result = SetSelection(savedSelOffset, savedSelLength);
1729 if (NS_FAILED(result))
1731 editor->EndTransaction();
1732 UNLOCK_DOC(this);
1733 return result;
1736 result = DeleteSelection();
1738 if (NS_FAILED(result))
1740 editor->EndTransaction();
1741 UNLOCK_DOC(this);
1742 return result;
1746 result = editor->EndTransaction();
1748 UNLOCK_DOC(this);
1750 return result;
1753 NS_IMETHODIMP
1754 nsTextServicesDocument::DidInsertNode(nsIDOMNode *aNode,
1755 nsIDOMNode *aParent,
1756 int32_t aPosition,
1757 nsresult aResult)
1759 return NS_OK;
1762 NS_IMETHODIMP
1763 nsTextServicesDocument::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
1765 NS_ENSURE_SUCCESS(aResult, NS_OK);
1767 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
1769 //**** KDEBUG ****
1770 // printf("** DeleteNode: 0x%.8x\n", aChild);
1771 // fflush(stdout);
1772 //**** KDEBUG ****
1774 LOCK_DOC(this);
1776 int32_t nodeIndex = 0;
1777 bool hasEntry = false;
1778 OffsetEntry *entry;
1780 nsresult result = NodeHasOffsetEntry(&mOffsetTable, aChild, &hasEntry, &nodeIndex);
1782 if (NS_FAILED(result))
1784 UNLOCK_DOC(this);
1785 return result;
1788 if (!hasEntry)
1790 // It's okay if the node isn't in the offset table, the
1791 // editor could be cleaning house.
1793 UNLOCK_DOC(this);
1794 return NS_OK;
1797 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mIterator->GetCurrentNode());
1799 if (node && node == aChild &&
1800 mIteratorStatus != nsTextServicesDocument::eIsDone)
1802 // XXX: This should never really happen because
1803 // AdjustContentIterator() should have been called prior
1804 // to the delete to try and position the iterator on the
1805 // next valid text node in the offset table, and if there
1806 // wasn't a next, it would've set mIteratorStatus to eIsDone.
1808 NS_ERROR("DeleteNode called for current iterator node.");
1811 int32_t tcount = mOffsetTable.Length();
1813 while (nodeIndex < tcount)
1815 entry = mOffsetTable[nodeIndex];
1817 if (!entry)
1819 UNLOCK_DOC(this);
1820 return NS_ERROR_FAILURE;
1823 if (entry->mNode == aChild)
1825 entry->mIsValid = false;
1828 nodeIndex++;
1831 UNLOCK_DOC(this);
1833 return NS_OK;
1836 NS_IMETHODIMP
1837 nsTextServicesDocument::DidSplitNode(nsIDOMNode *aExistingRightNode,
1838 int32_t aOffset,
1839 nsIDOMNode *aNewLeftNode,
1840 nsresult aResult)
1842 //**** KDEBUG ****
1843 // printf("** SplitNode: 0x%.8x %d 0x%.8x\n", aExistingRightNode, aOffset, aNewLeftNode);
1844 // fflush(stdout);
1845 //**** KDEBUG ****
1846 return NS_OK;
1849 NS_IMETHODIMP
1850 nsTextServicesDocument::DidJoinNodes(nsIDOMNode *aLeftNode,
1851 nsIDOMNode *aRightNode,
1852 nsIDOMNode *aParent,
1853 nsresult aResult)
1855 NS_ENSURE_SUCCESS(aResult, NS_OK);
1857 int32_t i;
1858 uint16_t type;
1859 nsresult result;
1861 //**** KDEBUG ****
1862 // printf("** JoinNodes: 0x%.8x 0x%.8x 0x%.8x\n", aLeftNode, aRightNode, aParent);
1863 // fflush(stdout);
1864 //**** KDEBUG ****
1866 // Make sure that both nodes are text nodes -- otherwise we don't care.
1868 result = aLeftNode->GetNodeType(&type);
1869 NS_ENSURE_SUCCESS(result, NS_OK);
1870 if (nsIDOMNode::TEXT_NODE != type) {
1871 return NS_OK;
1874 result = aRightNode->GetNodeType(&type);
1875 NS_ENSURE_SUCCESS(result, NS_OK);
1876 if (nsIDOMNode::TEXT_NODE != type) {
1877 return NS_OK;
1880 // Note: The editor merges the contents of the left node into the
1881 // contents of the right.
1883 int32_t leftIndex = 0;
1884 int32_t rightIndex = 0;
1885 bool leftHasEntry = false;
1886 bool rightHasEntry = false;
1888 result = NodeHasOffsetEntry(&mOffsetTable, aLeftNode, &leftHasEntry, &leftIndex);
1890 NS_ENSURE_SUCCESS(result, result);
1892 if (!leftHasEntry)
1894 // It's okay if the node isn't in the offset table, the
1895 // editor could be cleaning house.
1896 return NS_OK;
1899 result = NodeHasOffsetEntry(&mOffsetTable, aRightNode, &rightHasEntry, &rightIndex);
1901 NS_ENSURE_SUCCESS(result, result);
1903 if (!rightHasEntry)
1905 // It's okay if the node isn't in the offset table, the
1906 // editor could be cleaning house.
1907 return NS_OK;
1910 NS_ASSERTION(leftIndex < rightIndex, "Indexes out of order.");
1912 if (leftIndex > rightIndex)
1914 // Don't know how to handle this situation.
1915 return NS_ERROR_FAILURE;
1918 LOCK_DOC(this);
1920 OffsetEntry *entry = mOffsetTable[rightIndex];
1921 NS_ASSERTION(entry->mNodeOffset == 0, "Unexpected offset value for rightIndex.");
1923 // Run through the table and change all entries referring to
1924 // the left node so that they now refer to the right node:
1926 nsAutoString str;
1927 result = aLeftNode->GetNodeValue(str);
1928 int32_t nodeLength = str.Length();
1930 for (i = leftIndex; i < rightIndex; i++)
1932 entry = mOffsetTable[i];
1934 if (entry->mNode == aLeftNode)
1936 if (entry->mIsValid)
1937 entry->mNode = aRightNode;
1939 else
1940 break;
1943 // Run through the table and adjust the node offsets
1944 // for all entries referring to the right node.
1946 for (i = rightIndex; i < int32_t(mOffsetTable.Length()); i++)
1948 entry = mOffsetTable[i];
1950 if (entry->mNode == aRightNode)
1952 if (entry->mIsValid)
1953 entry->mNodeOffset += nodeLength;
1955 else
1956 break;
1959 // Now check to see if the iterator is pointing to the
1960 // left node. If it is, make it point to the right node!
1962 nsCOMPtr<nsIContent> leftContent = do_QueryInterface(aLeftNode);
1963 nsCOMPtr<nsIContent> rightContent = do_QueryInterface(aRightNode);
1965 if (!leftContent || !rightContent)
1967 UNLOCK_DOC(this);
1968 return NS_ERROR_FAILURE;
1971 if (mIterator->GetCurrentNode() == leftContent)
1972 result = mIterator->PositionAt(rightContent);
1974 UNLOCK_DOC(this);
1976 return NS_OK;
1979 nsresult
1980 nsTextServicesDocument::CreateContentIterator(nsIDOMRange *aRange, nsIContentIterator **aIterator)
1982 NS_ENSURE_TRUE(aRange && aIterator, NS_ERROR_NULL_POINTER);
1984 *aIterator = nullptr;
1986 // Create a nsFilteredContentIterator
1987 // This class wraps the ContentIterator in order to give itself a chance
1988 // to filter out certain content nodes
1989 nsRefPtr<nsFilteredContentIterator> filter = new nsFilteredContentIterator(mTxtSvcFilter);
1991 nsresult result = filter->Init(aRange);
1992 if (NS_FAILED(result)) {
1993 return result;
1996 filter.forget(aIterator);
1997 return NS_OK;
2000 nsresult
2001 nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode **aNode)
2003 nsresult result;
2005 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
2007 *aNode = 0;
2009 NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
2011 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDOMDocument);
2013 if (htmlDoc)
2015 // For HTML documents, the content root node is the body.
2017 nsCOMPtr<nsIDOMHTMLElement> bodyElement;
2019 result = htmlDoc->GetBody(getter_AddRefs(bodyElement));
2021 NS_ENSURE_SUCCESS(result, result);
2023 NS_ENSURE_TRUE(bodyElement, NS_ERROR_FAILURE);
2025 result = bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
2027 else
2029 // For non-HTML documents, the content root node will be the document element.
2031 nsCOMPtr<nsIDOMElement> docElement;
2033 result = mDOMDocument->GetDocumentElement(getter_AddRefs(docElement));
2035 NS_ENSURE_SUCCESS(result, result);
2037 NS_ENSURE_TRUE(docElement, NS_ERROR_FAILURE);
2039 result = docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
2042 return result;
2045 nsresult
2046 nsTextServicesDocument::CreateDocumentContentRange(nsIDOMRange **aRange)
2048 *aRange = nullptr;
2050 nsCOMPtr<nsIDOMNode> node;
2051 nsresult rv = GetDocumentContentRootNode(getter_AddRefs(node));
2052 NS_ENSURE_SUCCESS(rv, rv);
2053 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
2055 nsCOMPtr<nsINode> nativeNode = do_QueryInterface(node);
2056 NS_ENSURE_STATE(nativeNode);
2058 nsRefPtr<nsRange> range = new nsRange(nativeNode);
2060 rv = range->SelectNodeContents(node);
2061 NS_ENSURE_SUCCESS(rv, rv);
2063 range.forget(aRange);
2064 return NS_OK;
2067 nsresult
2068 nsTextServicesDocument::CreateDocumentContentRootToNodeOffsetRange(nsIDOMNode *aParent, int32_t aOffset, bool aToStart, nsIDOMRange **aRange)
2070 NS_ENSURE_TRUE(aParent && aRange, NS_ERROR_NULL_POINTER);
2072 *aRange = 0;
2074 NS_ASSERTION(aOffset >= 0, "Invalid offset!");
2076 if (aOffset < 0)
2077 return NS_ERROR_FAILURE;
2079 nsCOMPtr<nsIDOMNode> bodyNode;
2080 nsresult rv = GetDocumentContentRootNode(getter_AddRefs(bodyNode));
2081 NS_ENSURE_SUCCESS(rv, rv);
2082 NS_ENSURE_TRUE(bodyNode, NS_ERROR_NULL_POINTER);
2084 nsCOMPtr<nsIDOMNode> startNode;
2085 nsCOMPtr<nsIDOMNode> endNode;
2086 int32_t startOffset, endOffset;
2088 if (aToStart) {
2089 // The range should begin at the start of the document
2090 // and extend up until (aParent, aOffset).
2092 startNode = bodyNode;
2093 startOffset = 0;
2094 endNode = aParent;
2095 endOffset = aOffset;
2096 } else {
2097 // The range should begin at (aParent, aOffset) and
2098 // extend to the end of the document.
2100 startNode = aParent;
2101 startOffset = aOffset;
2102 endNode = bodyNode;
2104 nsCOMPtr<nsINode> body = do_QueryInterface(bodyNode);
2105 endOffset = body ? int32_t(body->GetChildCount()) : 0;
2108 return nsRange::CreateRange(startNode, startOffset, endNode, endOffset,
2109 aRange);
2112 nsresult
2113 nsTextServicesDocument::CreateDocumentContentIterator(nsIContentIterator **aIterator)
2115 nsresult result;
2117 NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
2119 nsCOMPtr<nsIDOMRange> range;
2121 result = CreateDocumentContentRange(getter_AddRefs(range));
2123 NS_ENSURE_SUCCESS(result, result);
2125 result = CreateContentIterator(range, aIterator);
2127 return result;
2130 nsresult
2131 nsTextServicesDocument::AdjustContentIterator()
2133 nsresult result = NS_OK;
2135 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
2137 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mIterator->GetCurrentNode()));
2139 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2141 nsIDOMNode *nodePtr = node.get();
2142 int32_t tcount = mOffsetTable.Length();
2144 nsIDOMNode *prevValidNode = 0;
2145 nsIDOMNode *nextValidNode = 0;
2146 bool foundEntry = false;
2147 OffsetEntry *entry;
2149 for (int32_t i = 0; i < tcount && !nextValidNode; i++)
2151 entry = mOffsetTable[i];
2153 NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
2155 if (entry->mNode == nodePtr)
2157 if (entry->mIsValid)
2159 // The iterator is still pointing to something valid!
2160 // Do nothing!
2162 return NS_OK;
2164 else
2166 // We found an invalid entry that points to
2167 // the current iterator node. Stop looking for
2168 // a previous valid node!
2170 foundEntry = true;
2174 if (entry->mIsValid)
2176 if (!foundEntry)
2177 prevValidNode = entry->mNode;
2178 else
2179 nextValidNode = entry->mNode;
2183 nsCOMPtr<nsIContent> content;
2185 if (prevValidNode)
2186 content = do_QueryInterface(prevValidNode);
2187 else if (nextValidNode)
2188 content = do_QueryInterface(nextValidNode);
2190 if (content)
2192 result = mIterator->PositionAt(content);
2194 if (NS_FAILED(result))
2195 mIteratorStatus = eIsDone;
2196 else
2197 mIteratorStatus = eValid;
2199 return result;
2202 // If we get here, there aren't any valid entries
2203 // in the offset table! Try to position the iterator
2204 // on the next text block first, then previous if
2205 // one doesn't exist!
2207 if (mNextTextBlock)
2209 result = mIterator->PositionAt(mNextTextBlock);
2211 if (NS_FAILED(result))
2213 mIteratorStatus = eIsDone;
2214 return result;
2217 mIteratorStatus = eNext;
2219 else if (mPrevTextBlock)
2221 result = mIterator->PositionAt(mPrevTextBlock);
2223 if (NS_FAILED(result))
2225 mIteratorStatus = eIsDone;
2226 return result;
2229 mIteratorStatus = ePrev;
2231 else
2232 mIteratorStatus = eIsDone;
2234 return NS_OK;
2237 bool
2238 nsTextServicesDocument::DidSkip(nsIContentIterator* aFilteredIter)
2240 // We can assume here that the Iterator is a nsFilteredContentIterator because
2241 // all the iterator are created in CreateContentIterator which create a
2242 // nsFilteredContentIterator
2243 // So if the iterator bailed on one of the "filtered" content nodes then we
2244 // consider that to be a block and bail with true
2245 if (aFilteredIter) {
2246 nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
2247 if (filter && filter->DidSkip()) {
2248 return true;
2251 return false;
2254 void
2255 nsTextServicesDocument::ClearDidSkip(nsIContentIterator* aFilteredIter)
2257 // Clear filter's skip flag
2258 if (aFilteredIter) {
2259 nsFilteredContentIterator* filter = static_cast<nsFilteredContentIterator *>(aFilteredIter);
2260 filter->ClearDidSkip();
2264 bool
2265 nsTextServicesDocument::IsBlockNode(nsIContent *aContent)
2267 if (!aContent) {
2268 NS_ERROR("How did a null pointer get passed to IsBlockNode?");
2269 return false;
2272 nsIAtom *atom = aContent->Tag();
2274 return (sAAtom != atom &&
2275 sAddressAtom != atom &&
2276 sBigAtom != atom &&
2277 sBAtom != atom &&
2278 sCiteAtom != atom &&
2279 sCodeAtom != atom &&
2280 sDfnAtom != atom &&
2281 sEmAtom != atom &&
2282 sFontAtom != atom &&
2283 sIAtom != atom &&
2284 sKbdAtom != atom &&
2285 sKeygenAtom != atom &&
2286 sNobrAtom != atom &&
2287 sSAtom != atom &&
2288 sSampAtom != atom &&
2289 sSmallAtom != atom &&
2290 sSpacerAtom != atom &&
2291 sSpanAtom != atom &&
2292 sStrikeAtom != atom &&
2293 sStrongAtom != atom &&
2294 sSubAtom != atom &&
2295 sSupAtom != atom &&
2296 sTtAtom != atom &&
2297 sUAtom != atom &&
2298 sVarAtom != atom &&
2299 sWbrAtom != atom);
2302 bool
2303 nsTextServicesDocument::HasSameBlockNodeParent(nsIContent *aContent1, nsIContent *aContent2)
2305 nsIContent* p1 = aContent1->GetParent();
2306 nsIContent* p2 = aContent2->GetParent();
2308 // Quick test:
2310 if (p1 == p2)
2311 return true;
2313 // Walk up the parent hierarchy looking for closest block boundary node:
2315 while (p1 && !IsBlockNode(p1))
2317 p1 = p1->GetParent();
2320 while (p2 && !IsBlockNode(p2))
2322 p2 = p2->GetParent();
2325 return p1 == p2;
2328 bool
2329 nsTextServicesDocument::IsTextNode(nsIContent *aContent)
2331 NS_ENSURE_TRUE(aContent, false);
2332 return nsIDOMNode::TEXT_NODE == aContent->NodeType();
2335 bool
2336 nsTextServicesDocument::IsTextNode(nsIDOMNode *aNode)
2338 NS_ENSURE_TRUE(aNode, false);
2340 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
2341 return IsTextNode(content);
2344 nsresult
2345 nsTextServicesDocument::SetSelectionInternal(int32_t aOffset, int32_t aLength, bool aDoUpdate)
2347 nsresult result = NS_OK;
2349 NS_ENSURE_TRUE(mSelCon && aOffset >= 0 && aLength >= 0, NS_ERROR_FAILURE);
2351 nsIDOMNode *sNode = 0, *eNode = 0;
2352 int32_t i, sOffset = 0, eOffset = 0;
2353 OffsetEntry *entry;
2355 // Find start of selection in node offset terms:
2357 for (i = 0; !sNode && i < int32_t(mOffsetTable.Length()); i++)
2359 entry = mOffsetTable[i];
2360 if (entry->mIsValid)
2362 if (entry->mIsInsertedText)
2364 // Caret can only be placed at the end of an
2365 // inserted text offset entry, if the offsets
2366 // match exactly!
2368 if (entry->mStrOffset == aOffset)
2370 sNode = entry->mNode;
2371 sOffset = entry->mNodeOffset + entry->mLength;
2374 else if (aOffset >= entry->mStrOffset)
2376 bool foundEntry = false;
2377 int32_t strEndOffset = entry->mStrOffset + entry->mLength;
2379 if (aOffset < strEndOffset)
2380 foundEntry = true;
2381 else if (aOffset == strEndOffset)
2383 // Peek after this entry to see if we have any
2384 // inserted text entries belonging to the same
2385 // entry->mNode. If so, we have to place the selection
2386 // after it!
2388 if ((i+1) < int32_t(mOffsetTable.Length()))
2390 OffsetEntry *nextEntry = mOffsetTable[i+1];
2392 if (!nextEntry->mIsValid || nextEntry->mStrOffset != aOffset)
2394 // Next offset entry isn't an exact match, so we'll
2395 // just use the current entry.
2396 foundEntry = true;
2401 if (foundEntry)
2403 sNode = entry->mNode;
2404 sOffset = entry->mNodeOffset + aOffset - entry->mStrOffset;
2408 if (sNode)
2410 mSelStartIndex = i;
2411 mSelStartOffset = aOffset;
2416 NS_ENSURE_TRUE(sNode, NS_ERROR_FAILURE);
2418 // XXX: If we ever get a SetSelection() method in nsIEditor, we should
2419 // use it.
2421 nsCOMPtr<nsISelection> selection;
2423 if (aDoUpdate)
2425 result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
2427 NS_ENSURE_SUCCESS(result, result);
2429 result = selection->Collapse(sNode, sOffset);
2431 NS_ENSURE_SUCCESS(result, result);
2434 if (aLength <= 0)
2436 // We have a collapsed selection. (Caret)
2438 mSelEndIndex = mSelStartIndex;
2439 mSelEndOffset = mSelStartOffset;
2441 //**** KDEBUG ****
2442 // printf("\n* Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
2443 //**** KDEBUG ****
2445 return NS_OK;
2448 // Find the end of the selection in node offset terms:
2450 int32_t endOffset = aOffset + aLength;
2452 for (i = mOffsetTable.Length() - 1; !eNode && i >= 0; i--)
2454 entry = mOffsetTable[i];
2456 if (entry->mIsValid)
2458 if (entry->mIsInsertedText)
2460 if (entry->mStrOffset == eOffset)
2462 // If the selection ends on an inserted text offset entry,
2463 // the selection includes the entire entry!
2465 eNode = entry->mNode;
2466 eOffset = entry->mNodeOffset + entry->mLength;
2469 else if (endOffset >= entry->mStrOffset && endOffset <= entry->mStrOffset + entry->mLength)
2471 eNode = entry->mNode;
2472 eOffset = entry->mNodeOffset + endOffset - entry->mStrOffset;
2475 if (eNode)
2477 mSelEndIndex = i;
2478 mSelEndOffset = endOffset;
2483 if (aDoUpdate && eNode)
2485 result = selection->Extend(eNode, eOffset);
2487 NS_ENSURE_SUCCESS(result, result);
2490 //**** KDEBUG ****
2491 // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
2492 //**** KDEBUG ****
2494 return result;
2497 nsresult
2498 nsTextServicesDocument::GetSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
2500 nsresult result;
2502 NS_ENSURE_TRUE(aSelStatus && aSelOffset && aSelLength, NS_ERROR_NULL_POINTER);
2504 *aSelStatus = nsITextServicesDocument::eBlockNotFound;
2505 *aSelOffset = -1;
2506 *aSelLength = -1;
2508 NS_ENSURE_TRUE(mDOMDocument && mSelCon, NS_ERROR_FAILURE);
2510 if (mIteratorStatus == nsTextServicesDocument::eIsDone)
2511 return NS_OK;
2513 nsCOMPtr<nsISelection> selection;
2514 bool isCollapsed;
2516 result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
2518 NS_ENSURE_SUCCESS(result, result);
2520 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2522 result = selection->GetIsCollapsed(&isCollapsed);
2524 NS_ENSURE_SUCCESS(result, result);
2526 // XXX: If we expose this method publicly, we need to
2527 // add LOCK_DOC/UNLOCK_DOC calls!
2529 // LOCK_DOC(this);
2531 if (isCollapsed)
2532 result = GetCollapsedSelection(aSelStatus, aSelOffset, aSelLength);
2533 else
2534 result = GetUncollapsedSelection(aSelStatus, aSelOffset, aSelLength);
2536 // UNLOCK_DOC(this);
2538 return result;
2541 nsresult
2542 nsTextServicesDocument::GetCollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
2544 nsCOMPtr<nsISelection> selection;
2545 nsresult result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
2546 NS_ENSURE_SUCCESS(result, result);
2547 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2549 // The calling function should have done the GetIsCollapsed()
2550 // check already. Just assume it's collapsed!
2551 *aSelStatus = nsITextServicesDocument::eBlockOutside;
2552 *aSelOffset = *aSelLength = -1;
2554 int32_t tableCount = mOffsetTable.Length();
2556 if (tableCount == 0)
2557 return NS_OK;
2559 // Get pointers to the first and last offset entries
2560 // in the table.
2562 OffsetEntry* eStart = mOffsetTable[0];
2563 OffsetEntry* eEnd;
2564 if (tableCount > 1)
2565 eEnd = mOffsetTable[tableCount - 1];
2566 else
2567 eEnd = eStart;
2569 int32_t eStartOffset = eStart->mNodeOffset;
2570 int32_t eEndOffset = eEnd->mNodeOffset + eEnd->mLength;
2572 nsCOMPtr<nsIDOMRange> range;
2573 result = selection->GetRangeAt(0, getter_AddRefs(range));
2574 NS_ENSURE_SUCCESS(result, result);
2576 nsCOMPtr<nsIDOMNode> domParent;
2577 result = range->GetStartContainer(getter_AddRefs(domParent));
2578 NS_ENSURE_SUCCESS(result, result);
2580 nsCOMPtr<nsINode> parent = do_QueryInterface(domParent);
2581 MOZ_ASSERT(parent);
2583 int32_t offset;
2584 result = range->GetStartOffset(&offset);
2585 NS_ENSURE_SUCCESS(result, result);
2587 int32_t e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
2588 domParent, offset);
2589 int32_t e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
2590 domParent, offset);
2592 if (e1s1 > 0 || e2s1 < 0) {
2593 // We're done if the caret is outside the current text block.
2594 return NS_OK;
2597 if (parent->NodeType() == nsIDOMNode::TEXT_NODE) {
2598 // Good news, the caret is in a text node. Look
2599 // through the offset table for the entry that
2600 // matches its parent and offset.
2602 for (int32_t i = 0; i < tableCount; i++) {
2603 OffsetEntry* entry = mOffsetTable[i];
2604 NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
2606 if (entry->mNode == domParent.get() &&
2607 entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
2609 *aSelStatus = nsITextServicesDocument::eBlockContains;
2610 *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
2611 *aSelLength = 0;
2613 return NS_OK;
2617 // If we get here, we didn't find a text node entry
2618 // in our offset table that matched.
2620 return NS_ERROR_FAILURE;
2623 // The caret is in our text block, but it's positioned in some
2624 // non-text node (ex. <b>). Create a range based on the start
2625 // and end of the text block, then create an iterator based on
2626 // this range, with its initial position set to the closest
2627 // child of this non-text node. Then look for the closest text
2628 // node.
2630 result = CreateRange(eStart->mNode, eStartOffset, eEnd->mNode, eEndOffset, getter_AddRefs(range));
2631 NS_ENSURE_SUCCESS(result, result);
2633 nsCOMPtr<nsIContentIterator> iter;
2634 result = CreateContentIterator(range, getter_AddRefs(iter));
2635 NS_ENSURE_SUCCESS(result, result);
2637 nsIContent* saveNode;
2638 if (parent->HasChildren()) {
2639 // XXX: We need to make sure that all of parent's
2640 // children are in the text block.
2642 // If the parent has children, position the iterator
2643 // on the child that is to the left of the offset.
2645 uint32_t childIndex = (uint32_t)offset;
2647 if (childIndex > 0) {
2648 uint32_t numChildren = parent->GetChildCount();
2649 NS_ASSERTION(childIndex <= numChildren, "Invalid selection offset!");
2651 if (childIndex > numChildren) {
2652 childIndex = numChildren;
2655 childIndex -= 1;
2658 nsIContent* content = parent->GetChildAt(childIndex);
2659 NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
2661 result = iter->PositionAt(content);
2662 NS_ENSURE_SUCCESS(result, result);
2664 saveNode = content;
2665 } else {
2666 // The parent has no children, so position the iterator
2667 // on the parent.
2668 NS_ENSURE_TRUE(parent->IsContent(), NS_ERROR_FAILURE);
2669 nsCOMPtr<nsIContent> content = parent->AsContent();
2671 result = iter->PositionAt(content);
2672 NS_ENSURE_SUCCESS(result, result);
2674 saveNode = content;
2677 // Now iterate to the left, towards the beginning of
2678 // the text block, to find the first text node you
2679 // come across.
2681 nsIContent* node = nullptr;
2682 while (!iter->IsDone()) {
2683 nsINode* current = iter->GetCurrentNode();
2684 if (current->NodeType() == nsIDOMNode::TEXT_NODE) {
2685 node = static_cast<nsIContent*>(current);
2686 break;
2689 iter->Prev();
2692 if (node) {
2693 // We found a node, now set the offset to the end
2694 // of the text node.
2695 offset = node->TextLength();
2696 } else {
2697 // We should never really get here, but I'm paranoid.
2699 // We didn't find a text node above, so iterate to
2700 // the right, towards the end of the text block, looking
2701 // for a text node.
2703 result = iter->PositionAt(saveNode);
2704 NS_ENSURE_SUCCESS(result, result);
2706 node = nullptr;
2707 while (!iter->IsDone()) {
2708 nsINode* current = iter->GetCurrentNode();
2710 if (current->NodeType() == nsIDOMNode::TEXT_NODE) {
2711 node = static_cast<nsIContent*>(current);
2712 break;
2715 iter->Next();
2718 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2720 // We found a text node, so set the offset to
2721 // the beginning of the node.
2723 offset = 0;
2726 for (int32_t i = 0; i < tableCount; i++) {
2727 OffsetEntry* entry = mOffsetTable[i];
2728 NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
2730 if (entry->mNode == node->AsDOMNode() &&
2731 entry->mNodeOffset <= offset && offset <= (entry->mNodeOffset + entry->mLength))
2733 *aSelStatus = nsITextServicesDocument::eBlockContains;
2734 *aSelOffset = entry->mStrOffset + (offset - entry->mNodeOffset);
2735 *aSelLength = 0;
2737 // Now move the caret so that it is actually in the text node.
2738 // We do this to keep things in sync.
2740 // In most cases, the user shouldn't see any movement in the caret
2741 // on screen.
2743 result = SetSelectionInternal(*aSelOffset, *aSelLength, true);
2745 return result;
2749 return NS_ERROR_FAILURE;
2752 nsresult
2753 nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus *aSelStatus, int32_t *aSelOffset, int32_t *aSelLength)
2755 nsresult result;
2757 nsCOMPtr<nsISelection> selection;
2758 nsCOMPtr<nsIDOMRange> range;
2759 OffsetEntry *entry;
2761 result = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
2763 NS_ENSURE_SUCCESS(result, result);
2765 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2767 // It is assumed that the calling function has made sure that the
2768 // selection is not collapsed, and that the input params to this
2769 // method are initialized to some defaults.
2771 nsCOMPtr<nsIDOMNode> startParent, endParent;
2772 int32_t startOffset, endOffset;
2773 int32_t rangeCount, tableCount, i;
2774 int32_t e1s1, e1s2, e2s1, e2s2;
2776 OffsetEntry *eStart, *eEnd;
2777 int32_t eStartOffset, eEndOffset;
2779 tableCount = mOffsetTable.Length();
2781 // Get pointers to the first and last offset entries
2782 // in the table.
2784 eStart = mOffsetTable[0];
2786 if (tableCount > 1)
2787 eEnd = mOffsetTable[tableCount - 1];
2788 else
2789 eEnd = eStart;
2791 eStartOffset = eStart->mNodeOffset;
2792 eEndOffset = eEnd->mNodeOffset + eEnd->mLength;
2794 result = selection->GetRangeCount(&rangeCount);
2796 NS_ENSURE_SUCCESS(result, result);
2798 // Find the first range in the selection that intersects
2799 // the current text block.
2801 for (i = 0; i < rangeCount; i++)
2803 result = selection->GetRangeAt(i, getter_AddRefs(range));
2805 NS_ENSURE_SUCCESS(result, result);
2807 result = GetRangeEndPoints(range,
2808 getter_AddRefs(startParent), &startOffset,
2809 getter_AddRefs(endParent), &endOffset);
2811 NS_ENSURE_SUCCESS(result, result);
2813 e1s2 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
2814 endParent, endOffset);
2815 e2s1 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
2816 startParent, startOffset);
2818 // Break out of the loop if the text block intersects the current range.
2820 if (e1s2 <= 0 && e2s1 >= 0)
2821 break;
2824 // We're done if we didn't find an intersecting range.
2826 if (rangeCount < 1 || e1s2 > 0 || e2s1 < 0)
2828 *aSelStatus = nsITextServicesDocument::eBlockOutside;
2829 *aSelOffset = *aSelLength = -1;
2830 return NS_OK;
2833 // Now that we have an intersecting range, find out more info:
2835 e1s1 = nsContentUtils::ComparePoints(eStart->mNode, eStartOffset,
2836 startParent, startOffset);
2837 e2s2 = nsContentUtils::ComparePoints(eEnd->mNode, eEndOffset,
2838 endParent, endOffset);
2840 if (rangeCount > 1)
2842 // There are multiple selection ranges, we only deal
2843 // with the first one that intersects the current,
2844 // text block, so mark this a as a partial.
2846 *aSelStatus = nsITextServicesDocument::eBlockPartial;
2848 else if (e1s1 > 0 && e2s2 < 0)
2850 // The range extends beyond the start and
2851 // end of the current text block.
2853 *aSelStatus = nsITextServicesDocument::eBlockInside;
2855 else if (e1s1 <= 0 && e2s2 >= 0)
2857 // The current text block contains the entire
2858 // range.
2860 *aSelStatus = nsITextServicesDocument::eBlockContains;
2862 else
2864 // The range partially intersects the block.
2866 *aSelStatus = nsITextServicesDocument::eBlockPartial;
2869 // Now create a range based on the intersection of the
2870 // text block and range:
2872 nsCOMPtr<nsIDOMNode> p1, p2;
2873 int32_t o1, o2;
2875 // The start of the range will be the rightmost
2876 // start node.
2878 if (e1s1 >= 0)
2880 p1 = do_QueryInterface(eStart->mNode);
2881 o1 = eStartOffset;
2883 else
2885 p1 = startParent;
2886 o1 = startOffset;
2889 // The end of the range will be the leftmost
2890 // end node.
2892 if (e2s2 <= 0)
2894 p2 = do_QueryInterface(eEnd->mNode);
2895 o2 = eEndOffset;
2897 else
2899 p2 = endParent;
2900 o2 = endOffset;
2903 result = CreateRange(p1, o1, p2, o2, getter_AddRefs(range));
2905 NS_ENSURE_SUCCESS(result, result);
2907 // Now iterate over this range to figure out the selection's
2908 // block offset and length.
2910 nsCOMPtr<nsIContentIterator> iter;
2912 result = CreateContentIterator(range, getter_AddRefs(iter));
2914 NS_ENSURE_SUCCESS(result, result);
2916 // Find the first text node in the range.
2918 bool found;
2919 nsCOMPtr<nsIContent> content;
2921 iter->First();
2923 if (!IsTextNode(p1))
2925 found = false;
2927 while (!iter->IsDone())
2929 content = do_QueryInterface(iter->GetCurrentNode());
2931 if (IsTextNode(content))
2933 p1 = do_QueryInterface(content);
2935 NS_ENSURE_TRUE(p1, NS_ERROR_FAILURE);
2937 o1 = 0;
2938 found = true;
2940 break;
2943 iter->Next();
2946 NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
2949 // Find the last text node in the range.
2951 iter->Last();
2953 if (! IsTextNode(p2))
2955 found = false;
2957 while (!iter->IsDone())
2959 content = do_QueryInterface(iter->GetCurrentNode());
2961 if (IsTextNode(content))
2963 p2 = do_QueryInterface(content);
2965 NS_ENSURE_TRUE(p2, NS_ERROR_FAILURE);
2967 nsString str;
2969 result = p2->GetNodeValue(str);
2971 NS_ENSURE_SUCCESS(result, result);
2973 o2 = str.Length();
2974 found = true;
2976 break;
2979 iter->Prev();
2982 NS_ENSURE_TRUE(found, NS_ERROR_FAILURE);
2985 found = false;
2986 *aSelLength = 0;
2988 for (i = 0; i < tableCount; i++)
2990 entry = mOffsetTable[i];
2992 NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
2994 if (!found)
2996 if (entry->mNode == p1.get() &&
2997 entry->mNodeOffset <= o1 && o1 <= (entry->mNodeOffset + entry->mLength))
2999 *aSelOffset = entry->mStrOffset + (o1 - entry->mNodeOffset);
3001 if (p1 == p2 &&
3002 entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
3004 // The start and end of the range are in the same offset
3005 // entry. Calculate the length of the range then we're done.
3007 *aSelLength = o2 - o1;
3008 break;
3010 else
3012 // Add the length of the sub string in this offset entry
3013 // that follows the start of the range.
3015 *aSelLength = entry->mLength - (o1 - entry->mNodeOffset);
3018 found = true;
3021 else // found
3023 if (entry->mNode == p2.get() &&
3024 entry->mNodeOffset <= o2 && o2 <= (entry->mNodeOffset + entry->mLength))
3026 // We found the end of the range. Calculate the length of the
3027 // sub string that is before the end of the range, then we're done.
3029 *aSelLength += o2 - entry->mNodeOffset;
3030 break;
3032 else
3034 // The entire entry must be in the range.
3036 *aSelLength += entry->mLength;
3041 return result;
3044 bool
3045 nsTextServicesDocument::SelectionIsCollapsed()
3047 return(mSelStartIndex == mSelEndIndex && mSelStartOffset == mSelEndOffset);
3050 bool
3051 nsTextServicesDocument::SelectionIsValid()
3053 return(mSelStartIndex >= 0);
3056 nsresult
3057 nsTextServicesDocument::GetRangeEndPoints(nsIDOMRange *aRange,
3058 nsIDOMNode **aStartParent, int32_t *aStartOffset,
3059 nsIDOMNode **aEndParent, int32_t *aEndOffset)
3061 nsresult result;
3063 NS_ENSURE_TRUE(aRange && aStartParent && aStartOffset && aEndParent && aEndOffset, NS_ERROR_NULL_POINTER);
3065 result = aRange->GetStartContainer(aStartParent);
3067 NS_ENSURE_SUCCESS(result, result);
3069 NS_ENSURE_TRUE(aStartParent, NS_ERROR_FAILURE);
3071 result = aRange->GetStartOffset(aStartOffset);
3073 NS_ENSURE_SUCCESS(result, result);
3075 result = aRange->GetEndContainer(aEndParent);
3077 NS_ENSURE_SUCCESS(result, result);
3079 NS_ENSURE_TRUE(aEndParent, NS_ERROR_FAILURE);
3081 result = aRange->GetEndOffset(aEndOffset);
3083 return result;
3087 nsresult
3088 nsTextServicesDocument::CreateRange(nsIDOMNode *aStartParent, int32_t aStartOffset,
3089 nsIDOMNode *aEndParent, int32_t aEndOffset,
3090 nsIDOMRange **aRange)
3092 return nsRange::CreateRange(aStartParent, aStartOffset, aEndParent,
3093 aEndOffset, aRange);
3096 nsresult
3097 nsTextServicesDocument::FirstTextNode(nsIContentIterator *aIterator,
3098 TSDIteratorStatus *aIteratorStatus)
3100 if (aIteratorStatus)
3101 *aIteratorStatus = nsTextServicesDocument::eIsDone;
3103 aIterator->First();
3105 while (!aIterator->IsDone()) {
3106 if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
3107 if (aIteratorStatus)
3108 *aIteratorStatus = nsTextServicesDocument::eValid;
3109 break;
3112 aIterator->Next();
3115 return NS_OK;
3118 nsresult
3119 nsTextServicesDocument::LastTextNode(nsIContentIterator *aIterator,
3120 TSDIteratorStatus *aIteratorStatus)
3122 if (aIteratorStatus)
3123 *aIteratorStatus = nsTextServicesDocument::eIsDone;
3125 aIterator->Last();
3127 while (!aIterator->IsDone()) {
3128 if (aIterator->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE) {
3129 if (aIteratorStatus)
3130 *aIteratorStatus = nsTextServicesDocument::eValid;
3131 break;
3134 aIterator->Prev();
3137 return NS_OK;
3140 nsresult
3141 nsTextServicesDocument::FirstTextNodeInCurrentBlock(nsIContentIterator *iter)
3143 NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
3145 ClearDidSkip(iter);
3147 nsCOMPtr<nsIContent> last;
3149 // Walk backwards over adjacent text nodes until
3150 // we hit a block boundary:
3152 while (!iter->IsDone())
3154 nsCOMPtr<nsIContent> content = iter->GetCurrentNode()->IsContent()
3155 ? iter->GetCurrentNode()->AsContent()
3156 : nullptr;
3158 if (IsTextNode(content))
3160 if (!last || HasSameBlockNodeParent(content, last))
3161 last = content;
3162 else
3164 // We're done, the current text node is in a
3165 // different block.
3166 break;
3169 else if (last && IsBlockNode(content))
3170 break;
3172 iter->Prev();
3174 if (DidSkip(iter))
3175 break;
3178 if (last)
3179 iter->PositionAt(last);
3181 // XXX: What should we return if last is null?
3183 return NS_OK;
3186 nsresult
3187 nsTextServicesDocument::FirstTextNodeInPrevBlock(nsIContentIterator *aIterator)
3189 nsCOMPtr<nsIContent> content;
3190 nsresult result;
3192 NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
3194 // XXX: What if mIterator is not currently on a text node?
3196 // Make sure mIterator is pointing to the first text node in the
3197 // current block:
3199 result = FirstTextNodeInCurrentBlock(aIterator);
3201 NS_ENSURE_SUCCESS(result, NS_ERROR_FAILURE);
3203 // Point mIterator to the first node before the first text node:
3205 aIterator->Prev();
3207 if (aIterator->IsDone())
3208 return NS_ERROR_FAILURE;
3210 // Now find the first text node of the next block:
3212 return FirstTextNodeInCurrentBlock(aIterator);
3215 nsresult
3216 nsTextServicesDocument::FirstTextNodeInNextBlock(nsIContentIterator *aIterator)
3218 nsCOMPtr<nsIContent> prev;
3219 bool crossedBlockBoundary = false;
3221 NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
3223 ClearDidSkip(aIterator);
3225 while (!aIterator->IsDone())
3227 nsCOMPtr<nsIContent> content = aIterator->GetCurrentNode()->IsContent()
3228 ? aIterator->GetCurrentNode()->AsContent()
3229 : nullptr;
3231 if (IsTextNode(content))
3233 if (!crossedBlockBoundary && (!prev || HasSameBlockNodeParent(prev, content)))
3234 prev = content;
3235 else
3236 break;
3238 else if (!crossedBlockBoundary && IsBlockNode(content))
3239 crossedBlockBoundary = true;
3241 aIterator->Next();
3243 if (!crossedBlockBoundary && DidSkip(aIterator))
3244 crossedBlockBoundary = true;
3247 return NS_OK;
3250 nsresult
3251 nsTextServicesDocument::GetFirstTextNodeInPrevBlock(nsIContent **aContent)
3253 nsresult result;
3255 NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
3257 *aContent = 0;
3259 // Save the iterator's current content node so we can restore
3260 // it when we are done:
3262 nsINode* node = mIterator->GetCurrentNode();
3264 result = FirstTextNodeInPrevBlock(mIterator);
3266 if (NS_FAILED(result))
3268 // Try to restore the iterator before returning.
3269 mIterator->PositionAt(node);
3270 return result;
3273 if (!mIterator->IsDone())
3275 nsCOMPtr<nsIContent> current = mIterator->GetCurrentNode()->IsContent()
3276 ? mIterator->GetCurrentNode()->AsContent()
3277 : nullptr;
3278 current.forget(aContent);
3281 // Restore the iterator:
3283 return mIterator->PositionAt(node);
3286 nsresult
3287 nsTextServicesDocument::GetFirstTextNodeInNextBlock(nsIContent **aContent)
3289 nsresult result;
3291 NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
3293 *aContent = 0;
3295 // Save the iterator's current content node so we can restore
3296 // it when we are done:
3298 nsINode* node = mIterator->GetCurrentNode();
3300 result = FirstTextNodeInNextBlock(mIterator);
3302 if (NS_FAILED(result))
3304 // Try to restore the iterator before returning.
3305 mIterator->PositionAt(node);
3306 return result;
3309 if (!mIterator->IsDone())
3311 nsCOMPtr<nsIContent> current = mIterator->GetCurrentNode()->IsContent()
3312 ? mIterator->GetCurrentNode()->AsContent()
3313 : nullptr;
3314 current.forget(aContent);
3317 // Restore the iterator:
3318 return mIterator->PositionAt(node);
3321 nsresult
3322 nsTextServicesDocument::CreateOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable,
3323 nsIContentIterator *aIterator,
3324 TSDIteratorStatus *aIteratorStatus,
3325 nsIDOMRange *aIterRange,
3326 nsString *aStr)
3328 nsresult result = NS_OK;
3330 nsCOMPtr<nsIContent> first;
3331 nsCOMPtr<nsIContent> prev;
3333 NS_ENSURE_TRUE(aIterator, NS_ERROR_NULL_POINTER);
3335 ClearOffsetTable(aOffsetTable);
3337 if (aStr)
3338 aStr->Truncate();
3340 if (*aIteratorStatus == nsTextServicesDocument::eIsDone)
3341 return NS_OK;
3343 // If we have an aIterRange, retrieve the endpoints so
3344 // they can be used in the while loop below to trim entries
3345 // for text nodes that are partially selected by aIterRange.
3347 nsCOMPtr<nsIDOMNode> rngStartNode, rngEndNode;
3348 int32_t rngStartOffset = 0, rngEndOffset = 0;
3350 if (aIterRange)
3352 result = GetRangeEndPoints(aIterRange,
3353 getter_AddRefs(rngStartNode), &rngStartOffset,
3354 getter_AddRefs(rngEndNode), &rngEndOffset);
3356 NS_ENSURE_SUCCESS(result, result);
3359 // The text service could have added text nodes to the beginning
3360 // of the current block and called this method again. Make sure
3361 // we really are at the beginning of the current block:
3363 result = FirstTextNodeInCurrentBlock(aIterator);
3365 NS_ENSURE_SUCCESS(result, result);
3367 int32_t offset = 0;
3369 ClearDidSkip(aIterator);
3371 while (!aIterator->IsDone())
3373 nsCOMPtr<nsIContent> content = aIterator->GetCurrentNode()->IsContent()
3374 ? aIterator->GetCurrentNode()->AsContent()
3375 : nullptr;
3377 if (IsTextNode(content))
3379 if (!prev || HasSameBlockNodeParent(prev, content))
3381 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
3383 if (node)
3385 nsString str;
3387 result = node->GetNodeValue(str);
3389 NS_ENSURE_SUCCESS(result, result);
3391 // Add an entry for this text node into the offset table:
3393 OffsetEntry *entry = new OffsetEntry(node, offset, str.Length());
3394 aOffsetTable->AppendElement(entry);
3396 // If one or both of the endpoints of the iteration range
3397 // are in the text node for this entry, make sure the entry
3398 // only accounts for the portion of the text node that is
3399 // in the range.
3401 int32_t startOffset = 0;
3402 int32_t endOffset = str.Length();
3403 bool adjustStr = false;
3405 if (entry->mNode == rngStartNode)
3407 entry->mNodeOffset = startOffset = rngStartOffset;
3408 adjustStr = true;
3411 if (entry->mNode == rngEndNode)
3413 endOffset = rngEndOffset;
3414 adjustStr = true;
3417 if (adjustStr)
3419 entry->mLength = endOffset - startOffset;
3420 str = Substring(str, startOffset, entry->mLength);
3423 offset += str.Length();
3425 if (aStr)
3427 // Append the text node's string to the output string:
3429 if (!first)
3430 *aStr = str;
3431 else
3432 *aStr += str;
3436 prev = content;
3438 if (!first)
3439 first = content;
3441 else
3442 break;
3445 else if (IsBlockNode(content))
3446 break;
3448 aIterator->Next();
3450 if (DidSkip(aIterator))
3451 break;
3454 if (first)
3456 // Always leave the iterator pointing at the first
3457 // text node of the current block!
3459 aIterator->PositionAt(first);
3461 else
3463 // If we never ran across a text node, the iterator
3464 // might have been pointing to something invalid to
3465 // begin with.
3467 *aIteratorStatus = nsTextServicesDocument::eIsDone;
3470 return result;
3473 nsresult
3474 nsTextServicesDocument::RemoveInvalidOffsetEntries()
3476 OffsetEntry *entry;
3477 int32_t i = 0;
3479 while (uint32_t(i) < mOffsetTable.Length())
3481 entry = mOffsetTable[i];
3483 if (!entry->mIsValid)
3485 mOffsetTable.RemoveElementAt(i);
3487 if (mSelStartIndex >= 0 && mSelStartIndex >= i)
3489 // We are deleting an entry that comes before
3490 // mSelStartIndex, decrement mSelStartIndex so
3491 // that it points to the correct entry!
3493 NS_ASSERTION(i != mSelStartIndex, "Invalid selection index.");
3495 --mSelStartIndex;
3496 --mSelEndIndex;
3499 else
3500 i++;
3503 return NS_OK;
3506 nsresult
3507 nsTextServicesDocument::ClearOffsetTable(nsTArray<OffsetEntry*> *aOffsetTable)
3509 uint32_t i;
3511 for (i = 0; i < aOffsetTable->Length(); i++)
3513 delete aOffsetTable->ElementAt(i);
3516 aOffsetTable->Clear();
3518 return NS_OK;
3521 nsresult
3522 nsTextServicesDocument::SplitOffsetEntry(int32_t aTableIndex, int32_t aNewEntryLength)
3524 OffsetEntry *entry = mOffsetTable[aTableIndex];
3526 NS_ASSERTION((aNewEntryLength > 0), "aNewEntryLength <= 0");
3527 NS_ASSERTION((aNewEntryLength < entry->mLength), "aNewEntryLength >= mLength");
3529 if (aNewEntryLength < 1 || aNewEntryLength >= entry->mLength)
3530 return NS_ERROR_FAILURE;
3532 int32_t oldLength = entry->mLength - aNewEntryLength;
3534 OffsetEntry *newEntry = new OffsetEntry(entry->mNode,
3535 entry->mStrOffset + oldLength,
3536 aNewEntryLength);
3538 if (!mOffsetTable.InsertElementAt(aTableIndex + 1, newEntry))
3540 delete newEntry;
3541 return NS_ERROR_FAILURE;
3544 // Adjust entry fields:
3546 entry->mLength = oldLength;
3547 newEntry->mNodeOffset = entry->mNodeOffset + oldLength;
3549 return NS_OK;
3552 nsresult
3553 nsTextServicesDocument::NodeHasOffsetEntry(nsTArray<OffsetEntry*> *aOffsetTable, nsIDOMNode *aNode, bool *aHasEntry, int32_t *aEntryIndex)
3555 OffsetEntry *entry;
3556 uint32_t i;
3558 NS_ENSURE_TRUE(aNode && aHasEntry && aEntryIndex, NS_ERROR_NULL_POINTER);
3560 for (i = 0; i < aOffsetTable->Length(); i++)
3562 entry = (*aOffsetTable)[i];
3564 NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
3566 if (entry->mNode == aNode)
3568 *aHasEntry = true;
3569 *aEntryIndex = i;
3571 return NS_OK;
3575 *aHasEntry = false;
3576 *aEntryIndex = -1;
3578 return NS_OK;
3581 // Spellchecker code has this. See bug 211343
3582 #define IS_NBSP_CHAR(c) (((unsigned char)0xa0)==(c))
3584 nsresult
3585 nsTextServicesDocument::FindWordBounds(nsTArray<OffsetEntry*> *aOffsetTable,
3586 nsString *aBlockStr,
3587 nsIDOMNode *aNode,
3588 int32_t aNodeOffset,
3589 nsIDOMNode **aWordStartNode,
3590 int32_t *aWordStartOffset,
3591 nsIDOMNode **aWordEndNode,
3592 int32_t *aWordEndOffset)
3594 // Initialize return values.
3596 if (aWordStartNode)
3597 *aWordStartNode = nullptr;
3598 if (aWordStartOffset)
3599 *aWordStartOffset = 0;
3600 if (aWordEndNode)
3601 *aWordEndNode = nullptr;
3602 if (aWordEndOffset)
3603 *aWordEndOffset = 0;
3605 int32_t entryIndex = 0;
3606 bool hasEntry = false;
3608 // It's assumed that aNode is a text node. The first thing
3609 // we do is get its index in the offset table so we can
3610 // calculate the dom point's string offset.
3612 nsresult result = NodeHasOffsetEntry(aOffsetTable, aNode, &hasEntry, &entryIndex);
3613 NS_ENSURE_SUCCESS(result, result);
3614 NS_ENSURE_TRUE(hasEntry, NS_ERROR_FAILURE);
3616 // Next we map aNodeOffset into a string offset.
3618 OffsetEntry *entry = (*aOffsetTable)[entryIndex];
3619 uint32_t strOffset = entry->mStrOffset + aNodeOffset - entry->mNodeOffset;
3621 // Now we use the word breaker to find the beginning and end
3622 // of the word from our calculated string offset.
3624 const char16_t *str = aBlockStr->get();
3625 uint32_t strLen = aBlockStr->Length();
3627 nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
3628 nsWordRange res = wordBreaker->FindWord(str, strLen, strOffset);
3629 if (res.mBegin > strLen) {
3630 return str ? NS_ERROR_ILLEGAL_VALUE : NS_ERROR_NULL_POINTER;
3633 // Strip out the NBSPs at the ends
3634 while ((res.mBegin <= res.mEnd) && (IS_NBSP_CHAR(str[res.mBegin])))
3635 res.mBegin++;
3636 if (str[res.mEnd] == (unsigned char)0x20)
3638 uint32_t realEndWord = res.mEnd - 1;
3639 while ((realEndWord > res.mBegin) && (IS_NBSP_CHAR(str[realEndWord])))
3640 realEndWord--;
3641 if (realEndWord < res.mEnd - 1)
3642 res.mEnd = realEndWord + 1;
3645 // Now that we have the string offsets for the beginning
3646 // and end of the word, run through the offset table and
3647 // convert them back into dom points.
3649 int32_t i, lastIndex = aOffsetTable->Length() - 1;
3651 for (i=0; i <= lastIndex; i++)
3653 entry = (*aOffsetTable)[i];
3655 int32_t strEndOffset = entry->mStrOffset + entry->mLength;
3657 // Check to see if res.mBegin is within the range covered
3658 // by this entry. Note that if res.mBegin is after the last
3659 // character covered by this entry, we will use the next
3660 // entry if there is one.
3662 if (uint32_t(entry->mStrOffset) <= res.mBegin &&
3663 (res.mBegin < uint32_t(strEndOffset) ||
3664 (res.mBegin == uint32_t(strEndOffset) && i == lastIndex)))
3666 if (aWordStartNode)
3668 *aWordStartNode = entry->mNode;
3669 NS_IF_ADDREF(*aWordStartNode);
3672 if (aWordStartOffset)
3673 *aWordStartOffset = entry->mNodeOffset + res.mBegin - entry->mStrOffset;
3675 if (!aWordEndNode && !aWordEndOffset)
3677 // We've found our start entry, but if we're not looking
3678 // for end entries, we're done.
3680 break;
3684 // Check to see if res.mEnd is within the range covered
3685 // by this entry.
3687 if (uint32_t(entry->mStrOffset) <= res.mEnd && res.mEnd <= uint32_t(strEndOffset))
3689 if (res.mBegin == res.mEnd && res.mEnd == uint32_t(strEndOffset) && i != lastIndex)
3691 // Wait for the next round so that we use the same entry
3692 // we did for aWordStartNode.
3694 continue;
3697 if (aWordEndNode)
3699 *aWordEndNode = entry->mNode;
3700 NS_IF_ADDREF(*aWordEndNode);
3703 if (aWordEndOffset)
3704 *aWordEndOffset = entry->mNodeOffset + res.mEnd - entry->mStrOffset;
3706 break;
3711 return NS_OK;
3714 NS_IMETHODIMP
3715 nsTextServicesDocument::WillInsertNode(nsIDOMNode *aNode,
3716 nsIDOMNode *aParent,
3717 int32_t aPosition)
3719 return NS_OK;
3722 NS_IMETHODIMP
3723 nsTextServicesDocument::WillDeleteNode(nsIDOMNode *aChild)
3725 return NS_OK;
3728 NS_IMETHODIMP
3729 nsTextServicesDocument::WillSplitNode(nsIDOMNode *aExistingRightNode,
3730 int32_t aOffset)
3732 return NS_OK;
3735 NS_IMETHODIMP
3736 nsTextServicesDocument::WillJoinNodes(nsIDOMNode *aLeftNode,
3737 nsIDOMNode *aRightNode,
3738 nsIDOMNode *aParent)
3740 return NS_OK;
3744 // -------------------------------
3745 // stubs for unused listen methods
3746 // -------------------------------
3748 NS_IMETHODIMP
3749 nsTextServicesDocument::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition)
3751 return NS_OK;
3754 NS_IMETHODIMP
3755 nsTextServicesDocument::DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition, nsresult aResult)
3757 return NS_OK;
3760 NS_IMETHODIMP
3761 nsTextServicesDocument::WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString)
3763 return NS_OK;
3766 NS_IMETHODIMP
3767 nsTextServicesDocument::DidInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString, nsresult aResult)
3769 return NS_OK;
3772 NS_IMETHODIMP
3773 nsTextServicesDocument::WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength)
3775 return NS_OK;
3778 NS_IMETHODIMP
3779 nsTextServicesDocument::DidDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength, nsresult aResult)
3781 return NS_OK;
3784 NS_IMETHODIMP
3785 nsTextServicesDocument::WillDeleteSelection(nsISelection *aSelection)
3787 return NS_OK;
3790 NS_IMETHODIMP
3791 nsTextServicesDocument::DidDeleteSelection(nsISelection *aSelection)
3793 return NS_OK;