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
42 #define UNLOCK_DOC(doc)
44 using namespace mozilla
;
49 OffsetEntry(nsIDOMNode
*aNode
, int32_t aOffset
, int32_t aLength
)
50 : mNode(aNode
), mNodeOffset(0), mStrOffset(aOffset
), mLength(aLength
),
51 mIsInsertedText(false), mIsValid(true)
60 virtual ~OffsetEntry()
77 #define TS_ATOM(name_, value_) nsIAtom* nsTextServicesDocument::name_ = 0;
78 #include "nsTSAtomList.h" // IWYU pragma: keep
81 nsTextServicesDocument::nsTextServicesDocument()
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
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
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
)
123 NS_IMPL_CYCLE_COLLECTION(nsTextServicesDocument
,
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
);
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
))
154 if (!selCon
|| (mSelCon
&& selCon
!= mSelCon
))
157 return NS_ERROR_FAILURE
;
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
))
174 if (!doc
|| (mDOMDocument
&& doc
!= mDOMDocument
))
177 return NS_ERROR_FAILURE
;
184 result
= CreateDocumentContentIterator(getter_AddRefs(mIterator
));
186 if (NS_FAILED(result
))
192 mIteratorStatus
= nsTextServicesDocument::eIsDone
;
194 result
= FirstBlock();
196 if (NS_FAILED(result
))
203 mEditor
= do_GetWeakReference(aEditor
);
205 result
= aEditor
->AddEditActionListener(this);
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
;
227 nsTextServicesDocument::SetExtent(nsIDOMRange
* aDOMRange
)
229 NS_ENSURE_ARG_POINTER(aDOMRange
);
230 NS_ENSURE_TRUE(mDOMDocument
, NS_ERROR_FAILURE
);
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
))
245 // Create a new iterator based on our new extent range.
247 result
= CreateContentIterator(mExtent
, getter_AddRefs(mIterator
));
249 if (NS_FAILED(result
))
255 // Now position the iterator at the start of the first block
258 mIteratorStatus
= nsTextServicesDocument::eIsDone
;
260 result
= FirstBlock();
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
),
280 getter_AddRefs(rngEndNode
),
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!
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
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
;
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
;
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
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
,
368 if (NS_FAILED(result
))
370 ClearOffsetTable(&offsetTable
);
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
392 result
= docIter
->PositionAt(lastText
);
393 NS_ENSURE_SUCCESS(result
, result
);
395 iterStatus
= nsTextServicesDocument::eValid
;
397 result
= CreateOffsetTable(&offsetTable
, docIter
, &iterStatus
,
399 if (NS_FAILED(result
))
401 ClearOffsetTable(&offsetTable
);
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
428 result
= aRange
->SetEnd(rngEndNode
, rngEndOffset
);
429 NS_ENSURE_SUCCESS(result
, result
);
431 return aRange
->SetStart(rngStartNode
, rngStartOffset
);
435 nsTextServicesDocument::SetFilter(nsITextServicesFilter
*aFilter
)
437 // Hang on to the filter so we can set it into the filtered iterator.
438 mTxtSvcFilter
= aFilter
;
444 nsTextServicesDocument::GetCurrentTextBlock(nsString
*aStr
)
448 NS_ENSURE_TRUE(aStr
, NS_ERROR_NULL_POINTER
);
452 NS_ENSURE_TRUE(mIterator
, NS_ERROR_FAILURE
);
456 result
= CreateOffsetTable(&mOffsetTable
, mIterator
, &mIteratorStatus
,
465 nsTextServicesDocument::FirstBlock()
467 NS_ENSURE_TRUE(mIterator
, NS_ERROR_FAILURE
);
471 nsresult result
= FirstTextNode(mIterator
, &mIteratorStatus
);
473 if (NS_FAILED(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
));
489 // There's no text block in the document!
491 mPrevTextBlock
= nullptr;
492 mNextTextBlock
= nullptr;
501 nsTextServicesDocument::LastSelectedBlock(TSDBlockSelectionStatus
*aSelStatus
,
505 nsresult result
= NS_OK
;
507 NS_ENSURE_TRUE(aSelStatus
&& aSelOffset
&& aSelLength
, NS_ERROR_NULL_POINTER
);
511 mIteratorStatus
= nsTextServicesDocument::eIsDone
;
513 *aSelStatus
= nsITextServicesDocument::eBlockNotFound
;
514 *aSelOffset
= *aSelLength
= -1;
516 if (!mSelCon
|| !mIterator
)
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
))
533 result
= selection
->GetIsCollapsed(&isCollapsed
);
535 if (NS_FAILED(result
))
541 nsCOMPtr
<nsIContentIterator
> iter
;
542 nsCOMPtr
<nsIDOMRange
> range
;
543 nsCOMPtr
<nsIDOMNode
> parent
;
544 int32_t i
, rangeCount
, offset
;
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
))
564 return NS_ERROR_FAILURE
;
567 result
= range
->GetStartContainer(getter_AddRefs(parent
));
569 if (NS_FAILED(result
))
578 return NS_ERROR_FAILURE
;
581 result
= range
->GetStartOffset(&offset
);
583 if (NS_FAILED(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
595 nsCOMPtr
<nsIContent
> content(do_QueryInterface(parent
));
600 return NS_ERROR_FAILURE
;
603 result
= mIterator
->PositionAt(content
);
605 if (NS_FAILED(result
))
611 result
= FirstTextNodeInCurrentBlock(mIterator
);
613 if (NS_FAILED(result
))
619 mIteratorStatus
= nsTextServicesDocument::eValid
;
621 result
= CreateOffsetTable(&mOffsetTable
, mIterator
, &mIteratorStatus
,
624 if (NS_FAILED(result
))
630 result
= GetSelection(aSelStatus
, aSelOffset
, aSelLength
);
632 if (NS_FAILED(result
))
638 if (*aSelStatus
== nsITextServicesDocument::eBlockContains
)
639 result
= SetSelectionInternal(*aSelOffset
, *aSelLength
, false);
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
))
656 result
= range
->GetCollapsed(&isCollapsed
);
658 if (NS_FAILED(result
))
666 // If we get here, the range is collapsed because there is nothing after
667 // the caret! Just return NS_OK;
673 result
= CreateContentIterator(range
, getter_AddRefs(iter
));
675 if (NS_FAILED(result
))
683 nsCOMPtr
<nsIContent
> content
;
684 while (!iter
->IsDone())
686 content
= do_QueryInterface(iter
->GetCurrentNode());
688 if (IsTextNode(content
))
702 result
= mIterator
->PositionAt(content
);
704 if (NS_FAILED(result
))
710 result
= FirstTextNodeInCurrentBlock(mIterator
);
712 if (NS_FAILED(result
))
718 mIteratorStatus
= nsTextServicesDocument::eValid
;
720 result
= CreateOffsetTable(&mOffsetTable
, mIterator
, &mIteratorStatus
,
723 if (NS_FAILED(result
))
729 result
= GetSelection(aSelStatus
, aSelOffset
, aSelLength
);
731 if (NS_FAILED(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
749 result
= selection
->GetRangeCount(&rangeCount
);
751 if (NS_FAILED(result
))
757 NS_ASSERTION(rangeCount
> 0, "Unexpected range count!");
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
))
780 // Create an iterator for the range.
782 result
= CreateContentIterator(range
, getter_AddRefs(iter
));
784 if (NS_FAILED(result
))
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
))
810 result
= FirstTextNodeInCurrentBlock(mIterator
);
812 if (NS_FAILED(result
))
818 mIteratorStatus
= nsTextServicesDocument::eValid
;
820 result
= CreateOffsetTable(&mOffsetTable
, mIterator
, &mIteratorStatus
,
823 if (NS_FAILED(result
))
829 result
= GetSelection(aSelStatus
, aSelOffset
, aSelLength
);
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
))
857 return NS_ERROR_FAILURE
;
860 result
= range
->GetEndContainer(getter_AddRefs(parent
));
862 if (NS_FAILED(result
))
871 return NS_ERROR_FAILURE
;
874 result
= range
->GetEndOffset(&offset
);
876 if (NS_FAILED(result
))
882 result
= CreateDocumentContentRootToNodeOffsetRange(parent
, offset
, false, getter_AddRefs(range
));
884 if (NS_FAILED(result
))
890 result
= range
->GetCollapsed(&isCollapsed
);
892 if (NS_FAILED(result
))
900 // If we get here, the range is collapsed because there is nothing after
901 // the current selection! Just return NS_OK;
907 result
= CreateContentIterator(range
, getter_AddRefs(iter
));
909 if (NS_FAILED(result
))
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
))
932 result
= FirstTextNodeInCurrentBlock(mIterator
);
934 if (NS_FAILED(result
))
941 mIteratorStatus
= nsTextServicesDocument::eValid
;
943 result
= CreateOffsetTable(&mOffsetTable
, mIterator
, &mIteratorStatus
,
946 if (NS_FAILED(result
))
952 result
= GetSelection(aSelStatus
, aSelOffset
, aSelLength
);
962 // If we get here, we didn't find any block before or inside
963 // the selection! Just return OK.
971 nsTextServicesDocument::PrevBlock()
973 nsresult result
= NS_OK
;
975 NS_ENSURE_TRUE(mIterator
, NS_ERROR_FAILURE
);
979 if (mIteratorStatus
== nsTextServicesDocument::eIsDone
)
982 switch (mIteratorStatus
)
984 case nsTextServicesDocument::eValid
:
985 case nsTextServicesDocument::eNext
:
987 result
= FirstTextNodeInPrevBlock(mIterator
);
989 if (NS_FAILED(result
))
991 mIteratorStatus
= nsTextServicesDocument::eIsDone
;
996 if (mIterator
->IsDone())
998 mIteratorStatus
= nsTextServicesDocument::eIsDone
;
1003 mIteratorStatus
= nsTextServicesDocument::eValid
;
1006 case nsTextServicesDocument::ePrev
:
1008 // The iterator already points to the previous
1009 // block, so don't do anything.
1011 mIteratorStatus
= nsTextServicesDocument::eValid
;
1016 mIteratorStatus
= nsTextServicesDocument::eIsDone
;
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
));
1032 mPrevTextBlock
= nullptr;
1033 mNextTextBlock
= nullptr;
1042 nsTextServicesDocument::NextBlock()
1044 nsresult result
= NS_OK
;
1046 NS_ENSURE_TRUE(mIterator
, NS_ERROR_FAILURE
);
1050 if (mIteratorStatus
== nsTextServicesDocument::eIsDone
)
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
;
1068 if (mIterator
->IsDone())
1070 mIteratorStatus
= nsTextServicesDocument::eIsDone
;
1075 mIteratorStatus
= nsTextServicesDocument::eValid
;
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
;
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!
1094 mIteratorStatus
= nsTextServicesDocument::eIsDone
;
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
));
1110 mPrevTextBlock
= nullptr;
1111 mNextTextBlock
= nullptr;
1121 nsTextServicesDocument::IsDone(bool *aIsDone
)
1123 NS_ENSURE_TRUE(aIsDone
, NS_ERROR_NULL_POINTER
);
1127 NS_ENSURE_TRUE(mIterator
, NS_ERROR_FAILURE
);
1131 *aIsDone
= (mIteratorStatus
== nsTextServicesDocument::eIsDone
) ? true : false;
1139 nsTextServicesDocument::SetSelection(int32_t aOffset
, int32_t aLength
)
1143 NS_ENSURE_TRUE(mSelCon
&& aOffset
>= 0 && aLength
>= 0, NS_ERROR_FAILURE
);
1147 result
= SetSelectionInternal(aOffset
, aLength
, true);
1152 // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1159 nsTextServicesDocument::ScrollSelectionIntoView()
1163 NS_ENSURE_TRUE(mSelCon
, NS_ERROR_FAILURE
);
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
);
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())
1196 // printf("\n---- Before Delete\n");
1197 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1198 // PrintOffsetTable();
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;
1210 result
= GetRangeEndPoints(mExtent
,
1211 getter_AddRefs(origStartNode
), &origStartOffset
,
1212 getter_AddRefs(origEndNode
), &origEndOffset
);
1214 if (NS_FAILED(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!
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
))
1261 // Adjust selection indexes to account for new entry:
1267 entry
= mOffsetTable
[i
];
1271 if (selLength
> 0 && mSelStartIndex
< mSelEndIndex
)
1273 // The entire entry is contained in the selection. Mark the
1276 entry
->mIsValid
= false;
1281 // printf("\n---- Middle Delete\n");
1282 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1283 // PrintOffsetTable();
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;
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
))
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
1330 entry
->mIsValid
= false;
1335 if (i
!= mSelStartIndex
&& i
!= mSelEndIndex
)
1337 // The entire entry is contained in the selection. Mark the
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
))
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
))
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()
1394 // Create the new iterator.
1396 result
= CreateContentIterator(mExtent
, getter_AddRefs(mIterator
));
1398 if (NS_FAILED(result
))
1404 // Now make the new iterator point to the content node
1405 // the old one was pointing at.
1409 result
= mIterator
->PositionAt(curContent
);
1411 if (NS_FAILED(result
))
1412 mIteratorStatus
= eIsDone
;
1414 mIteratorStatus
= eValid
;
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
)
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
)
1448 mSelStartIndex
= mSelEndIndex
= i
;
1449 mSelStartOffset
= mSelEndOffset
= entry
->mStrOffset
;
1454 result
= SetSelection(mSelStartOffset
, 0);
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();
1469 // printf("\n---- After Delete\n");
1470 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1471 // PrintOffsetTable();
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
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
1508 result
= SetSelection(mSelStartOffset
, 0);
1510 NS_ENSURE_SUCCESS(result
, result
);
1516 result
= editor
->BeginTransaction();
1518 if (NS_FAILED(result
))
1524 nsCOMPtr
<nsIPlaintextEditor
> textEditor (do_QueryInterface(editor
, &result
));
1526 result
= textEditor
->InsertText(*aText
);
1528 if (NS_FAILED(result
))
1530 editor
->EndTransaction();
1536 // printf("\n---- Before Insert\n");
1537 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1538 // PrintOffsetTable();
1541 int32_t strLength
= aText
->Length();
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
;
1562 // Insert an inserted text offset entry before the current
1565 itEntry
= new OffsetEntry(entry
->mNode
, entry
->mStrOffset
, strLength
);
1569 editor
->EndTransaction();
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();
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;
1595 if (mOffsetTable
.Length() > i
)
1597 itEntry
= mOffsetTable
[i
];
1601 editor
->EndTransaction();
1603 return NS_ERROR_FAILURE
;
1606 // Check if the entry is a match. If it isn't, set
1609 if (!itEntry
->mIsInsertedText
|| itEntry
->mStrOffset
!= mSelStartOffset
)
1615 // We didn't find an inserted text offset entry, so
1618 itEntry
= new OffsetEntry(entry
->mNode
, mSelStartOffset
, 0);
1622 editor
->EndTransaction();
1624 return NS_ERROR_OUT_OF_MEMORY
;
1627 itEntry
->mNodeOffset
= entry
->mNodeOffset
+ entry
->mLength
;
1628 itEntry
->mIsInsertedText
= true;
1630 if (!mOffsetTable
.InsertElementAt(i
, 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();
1654 result
= selection
->Collapse(itEntry
->mNode
, itEntry
->mNodeOffset
+ itEntry
->mLength
);
1656 if (NS_FAILED(result
))
1658 editor
->EndTransaction();
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();
1680 itEntry
= new OffsetEntry(entry
->mNode
, mSelStartOffset
, strLength
);
1684 editor
->EndTransaction();
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();
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
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
;
1720 // printf("\n---- After Insert\n");
1721 // printf("Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
1722 // PrintOffsetTable();
1725 if (!collapsedSelection
)
1727 result
= SetSelection(savedSelOffset
, savedSelLength
);
1729 if (NS_FAILED(result
))
1731 editor
->EndTransaction();
1736 result
= DeleteSelection();
1738 if (NS_FAILED(result
))
1740 editor
->EndTransaction();
1746 result
= editor
->EndTransaction();
1754 nsTextServicesDocument::DidInsertNode(nsIDOMNode
*aNode
,
1755 nsIDOMNode
*aParent
,
1763 nsTextServicesDocument::DidDeleteNode(nsIDOMNode
*aChild
, nsresult aResult
)
1765 NS_ENSURE_SUCCESS(aResult
, NS_OK
);
1767 NS_ENSURE_TRUE(mIterator
, NS_ERROR_FAILURE
);
1770 // printf("** DeleteNode: 0x%.8x\n", aChild);
1776 int32_t nodeIndex
= 0;
1777 bool hasEntry
= false;
1780 nsresult result
= NodeHasOffsetEntry(&mOffsetTable
, aChild
, &hasEntry
, &nodeIndex
);
1782 if (NS_FAILED(result
))
1790 // It's okay if the node isn't in the offset table, the
1791 // editor could be cleaning house.
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
];
1820 return NS_ERROR_FAILURE
;
1823 if (entry
->mNode
== aChild
)
1825 entry
->mIsValid
= false;
1837 nsTextServicesDocument::DidSplitNode(nsIDOMNode
*aExistingRightNode
,
1839 nsIDOMNode
*aNewLeftNode
,
1843 // printf("** SplitNode: 0x%.8x %d 0x%.8x\n", aExistingRightNode, aOffset, aNewLeftNode);
1850 nsTextServicesDocument::DidJoinNodes(nsIDOMNode
*aLeftNode
,
1851 nsIDOMNode
*aRightNode
,
1852 nsIDOMNode
*aParent
,
1855 NS_ENSURE_SUCCESS(aResult
, NS_OK
);
1862 // printf("** JoinNodes: 0x%.8x 0x%.8x 0x%.8x\n", aLeftNode, aRightNode, aParent);
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
) {
1874 result
= aRightNode
->GetNodeType(&type
);
1875 NS_ENSURE_SUCCESS(result
, NS_OK
);
1876 if (nsIDOMNode::TEXT_NODE
!= type
) {
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
);
1894 // It's okay if the node isn't in the offset table, the
1895 // editor could be cleaning house.
1899 result
= NodeHasOffsetEntry(&mOffsetTable
, aRightNode
, &rightHasEntry
, &rightIndex
);
1901 NS_ENSURE_SUCCESS(result
, result
);
1905 // It's okay if the node isn't in the offset table, the
1906 // editor could be cleaning house.
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
;
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:
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
;
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
;
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
)
1968 return NS_ERROR_FAILURE
;
1971 if (mIterator
->GetCurrentNode() == leftContent
)
1972 result
= mIterator
->PositionAt(rightContent
);
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
)) {
1996 filter
.forget(aIterator
);
2001 nsTextServicesDocument::GetDocumentContentRootNode(nsIDOMNode
**aNode
)
2005 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
2009 NS_ENSURE_TRUE(mDOMDocument
, NS_ERROR_FAILURE
);
2011 nsCOMPtr
<nsIDOMHTMLDocument
> htmlDoc
= do_QueryInterface(mDOMDocument
);
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
);
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
);
2046 nsTextServicesDocument::CreateDocumentContentRange(nsIDOMRange
**aRange
)
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
);
2068 nsTextServicesDocument::CreateDocumentContentRootToNodeOffsetRange(nsIDOMNode
*aParent
, int32_t aOffset
, bool aToStart
, nsIDOMRange
**aRange
)
2070 NS_ENSURE_TRUE(aParent
&& aRange
, NS_ERROR_NULL_POINTER
);
2074 NS_ASSERTION(aOffset
>= 0, "Invalid offset!");
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
;
2089 // The range should begin at the start of the document
2090 // and extend up until (aParent, aOffset).
2092 startNode
= bodyNode
;
2095 endOffset
= aOffset
;
2097 // The range should begin at (aParent, aOffset) and
2098 // extend to the end of the document.
2100 startNode
= aParent
;
2101 startOffset
= aOffset
;
2104 nsCOMPtr
<nsINode
> body
= do_QueryInterface(bodyNode
);
2105 endOffset
= body
? int32_t(body
->GetChildCount()) : 0;
2108 return nsRange::CreateRange(startNode
, startOffset
, endNode
, endOffset
,
2113 nsTextServicesDocument::CreateDocumentContentIterator(nsIContentIterator
**aIterator
)
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
);
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;
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!
2166 // We found an invalid entry that points to
2167 // the current iterator node. Stop looking for
2168 // a previous valid node!
2174 if (entry
->mIsValid
)
2177 prevValidNode
= entry
->mNode
;
2179 nextValidNode
= entry
->mNode
;
2183 nsCOMPtr
<nsIContent
> content
;
2186 content
= do_QueryInterface(prevValidNode
);
2187 else if (nextValidNode
)
2188 content
= do_QueryInterface(nextValidNode
);
2192 result
= mIterator
->PositionAt(content
);
2194 if (NS_FAILED(result
))
2195 mIteratorStatus
= eIsDone
;
2197 mIteratorStatus
= eValid
;
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!
2209 result
= mIterator
->PositionAt(mNextTextBlock
);
2211 if (NS_FAILED(result
))
2213 mIteratorStatus
= eIsDone
;
2217 mIteratorStatus
= eNext
;
2219 else if (mPrevTextBlock
)
2221 result
= mIterator
->PositionAt(mPrevTextBlock
);
2223 if (NS_FAILED(result
))
2225 mIteratorStatus
= eIsDone
;
2229 mIteratorStatus
= ePrev
;
2232 mIteratorStatus
= eIsDone
;
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()) {
2255 nsTextServicesDocument::ClearDidSkip(nsIContentIterator
* aFilteredIter
)
2257 // Clear filter's skip flag
2258 if (aFilteredIter
) {
2259 nsFilteredContentIterator
* filter
= static_cast<nsFilteredContentIterator
*>(aFilteredIter
);
2260 filter
->ClearDidSkip();
2265 nsTextServicesDocument::IsBlockNode(nsIContent
*aContent
)
2268 NS_ERROR("How did a null pointer get passed to IsBlockNode?");
2272 nsIAtom
*atom
= aContent
->Tag();
2274 return (sAAtom
!= atom
&&
2275 sAddressAtom
!= atom
&&
2278 sCiteAtom
!= atom
&&
2279 sCodeAtom
!= atom
&&
2282 sFontAtom
!= atom
&&
2285 sKeygenAtom
!= atom
&&
2286 sNobrAtom
!= atom
&&
2288 sSampAtom
!= atom
&&
2289 sSmallAtom
!= atom
&&
2290 sSpacerAtom
!= atom
&&
2291 sSpanAtom
!= atom
&&
2292 sStrikeAtom
!= atom
&&
2293 sStrongAtom
!= atom
&&
2303 nsTextServicesDocument::HasSameBlockNodeParent(nsIContent
*aContent1
, nsIContent
*aContent2
)
2305 nsIContent
* p1
= aContent1
->GetParent();
2306 nsIContent
* p2
= aContent2
->GetParent();
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();
2329 nsTextServicesDocument::IsTextNode(nsIContent
*aContent
)
2331 NS_ENSURE_TRUE(aContent
, false);
2332 return nsIDOMNode::TEXT_NODE
== aContent
->NodeType();
2336 nsTextServicesDocument::IsTextNode(nsIDOMNode
*aNode
)
2338 NS_ENSURE_TRUE(aNode
, false);
2340 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
2341 return IsTextNode(content
);
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;
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
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
)
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
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.
2403 sNode
= entry
->mNode
;
2404 sOffset
= entry
->mNodeOffset
+ aOffset
- entry
->mStrOffset
;
2411 mSelStartOffset
= aOffset
;
2416 NS_ENSURE_TRUE(sNode
, NS_ERROR_FAILURE
);
2418 // XXX: If we ever get a SetSelection() method in nsIEditor, we should
2421 nsCOMPtr
<nsISelection
> selection
;
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
);
2436 // We have a collapsed selection. (Caret)
2438 mSelEndIndex
= mSelStartIndex
;
2439 mSelEndOffset
= mSelStartOffset
;
2442 // printf("\n* Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
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
;
2478 mSelEndOffset
= endOffset
;
2483 if (aDoUpdate
&& eNode
)
2485 result
= selection
->Extend(eNode
, eOffset
);
2487 NS_ENSURE_SUCCESS(result
, result
);
2491 // printf("\n * Sel: (%2d, %4d) (%2d, %4d)\n", mSelStartIndex, mSelStartOffset, mSelEndIndex, mSelEndOffset);
2498 nsTextServicesDocument::GetSelection(nsITextServicesDocument::TSDBlockSelectionStatus
*aSelStatus
, int32_t *aSelOffset
, int32_t *aSelLength
)
2502 NS_ENSURE_TRUE(aSelStatus
&& aSelOffset
&& aSelLength
, NS_ERROR_NULL_POINTER
);
2504 *aSelStatus
= nsITextServicesDocument::eBlockNotFound
;
2508 NS_ENSURE_TRUE(mDOMDocument
&& mSelCon
, NS_ERROR_FAILURE
);
2510 if (mIteratorStatus
== nsTextServicesDocument::eIsDone
)
2513 nsCOMPtr
<nsISelection
> selection
;
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!
2532 result
= GetCollapsedSelection(aSelStatus
, aSelOffset
, aSelLength
);
2534 result
= GetUncollapsedSelection(aSelStatus
, aSelOffset
, aSelLength
);
2536 // UNLOCK_DOC(this);
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)
2559 // Get pointers to the first and last offset entries
2562 OffsetEntry
* eStart
= mOffsetTable
[0];
2565 eEnd
= mOffsetTable
[tableCount
- 1];
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
);
2584 result
= range
->GetStartOffset(&offset
);
2585 NS_ENSURE_SUCCESS(result
, result
);
2587 int32_t e1s1
= nsContentUtils::ComparePoints(eStart
->mNode
, eStartOffset
,
2589 int32_t e2s1
= nsContentUtils::ComparePoints(eEnd
->mNode
, eEndOffset
,
2592 if (e1s1
> 0 || e2s1
< 0) {
2593 // We're done if the caret is outside the current text block.
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
);
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
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
;
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
);
2666 // The parent has no children, so position the iterator
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
);
2677 // Now iterate to the left, towards the beginning of
2678 // the text block, to find the first text node you
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
);
2693 // We found a node, now set the offset to the end
2694 // of the text node.
2695 offset
= node
->TextLength();
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
2703 result
= iter
->PositionAt(saveNode
);
2704 NS_ENSURE_SUCCESS(result
, result
);
2707 while (!iter
->IsDone()) {
2708 nsINode
* current
= iter
->GetCurrentNode();
2710 if (current
->NodeType() == nsIDOMNode::TEXT_NODE
) {
2711 node
= static_cast<nsIContent
*>(current
);
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.
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
);
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
2743 result
= SetSelectionInternal(*aSelOffset
, *aSelLength
, true);
2749 return NS_ERROR_FAILURE
;
2753 nsTextServicesDocument::GetUncollapsedSelection(nsITextServicesDocument::TSDBlockSelectionStatus
*aSelStatus
, int32_t *aSelOffset
, int32_t *aSelLength
)
2757 nsCOMPtr
<nsISelection
> selection
;
2758 nsCOMPtr
<nsIDOMRange
> range
;
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
2784 eStart
= mOffsetTable
[0];
2787 eEnd
= mOffsetTable
[tableCount
- 1];
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)
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;
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
);
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
2860 *aSelStatus
= nsITextServicesDocument::eBlockContains
;
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
;
2875 // The start of the range will be the rightmost
2880 p1
= do_QueryInterface(eStart
->mNode
);
2889 // The end of the range will be the leftmost
2894 p2
= do_QueryInterface(eEnd
->mNode
);
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.
2919 nsCOMPtr
<nsIContent
> content
;
2923 if (!IsTextNode(p1
))
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
);
2946 NS_ENSURE_TRUE(found
, NS_ERROR_FAILURE
);
2949 // Find the last text node in the range.
2953 if (! IsTextNode(p2
))
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
);
2969 result
= p2
->GetNodeValue(str
);
2971 NS_ENSURE_SUCCESS(result
, result
);
2982 NS_ENSURE_TRUE(found
, NS_ERROR_FAILURE
);
2988 for (i
= 0; i
< tableCount
; i
++)
2990 entry
= mOffsetTable
[i
];
2992 NS_ENSURE_TRUE(entry
, NS_ERROR_FAILURE
);
2996 if (entry
->mNode
== p1
.get() &&
2997 entry
->mNodeOffset
<= o1
&& o1
<= (entry
->mNodeOffset
+ entry
->mLength
))
2999 *aSelOffset
= entry
->mStrOffset
+ (o1
- entry
->mNodeOffset
);
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
;
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
);
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
;
3034 // The entire entry must be in the range.
3036 *aSelLength
+= entry
->mLength
;
3045 nsTextServicesDocument::SelectionIsCollapsed()
3047 return(mSelStartIndex
== mSelEndIndex
&& mSelStartOffset
== mSelEndOffset
);
3051 nsTextServicesDocument::SelectionIsValid()
3053 return(mSelStartIndex
>= 0);
3057 nsTextServicesDocument::GetRangeEndPoints(nsIDOMRange
*aRange
,
3058 nsIDOMNode
**aStartParent
, int32_t *aStartOffset
,
3059 nsIDOMNode
**aEndParent
, int32_t *aEndOffset
)
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
);
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
);
3097 nsTextServicesDocument::FirstTextNode(nsIContentIterator
*aIterator
,
3098 TSDIteratorStatus
*aIteratorStatus
)
3100 if (aIteratorStatus
)
3101 *aIteratorStatus
= nsTextServicesDocument::eIsDone
;
3105 while (!aIterator
->IsDone()) {
3106 if (aIterator
->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE
) {
3107 if (aIteratorStatus
)
3108 *aIteratorStatus
= nsTextServicesDocument::eValid
;
3119 nsTextServicesDocument::LastTextNode(nsIContentIterator
*aIterator
,
3120 TSDIteratorStatus
*aIteratorStatus
)
3122 if (aIteratorStatus
)
3123 *aIteratorStatus
= nsTextServicesDocument::eIsDone
;
3127 while (!aIterator
->IsDone()) {
3128 if (aIterator
->GetCurrentNode()->NodeType() == nsIDOMNode::TEXT_NODE
) {
3129 if (aIteratorStatus
)
3130 *aIteratorStatus
= nsTextServicesDocument::eValid
;
3141 nsTextServicesDocument::FirstTextNodeInCurrentBlock(nsIContentIterator
*iter
)
3143 NS_ENSURE_TRUE(iter
, NS_ERROR_NULL_POINTER
);
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()
3158 if (IsTextNode(content
))
3160 if (!last
|| HasSameBlockNodeParent(content
, last
))
3164 // We're done, the current text node is in a
3169 else if (last
&& IsBlockNode(content
))
3179 iter
->PositionAt(last
);
3181 // XXX: What should we return if last is null?
3187 nsTextServicesDocument::FirstTextNodeInPrevBlock(nsIContentIterator
*aIterator
)
3189 nsCOMPtr
<nsIContent
> content
;
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
3199 result
= FirstTextNodeInCurrentBlock(aIterator
);
3201 NS_ENSURE_SUCCESS(result
, NS_ERROR_FAILURE
);
3203 // Point mIterator to the first node before the first text node:
3207 if (aIterator
->IsDone())
3208 return NS_ERROR_FAILURE
;
3210 // Now find the first text node of the next block:
3212 return FirstTextNodeInCurrentBlock(aIterator
);
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()
3231 if (IsTextNode(content
))
3233 if (!crossedBlockBoundary
&& (!prev
|| HasSameBlockNodeParent(prev
, content
)))
3238 else if (!crossedBlockBoundary
&& IsBlockNode(content
))
3239 crossedBlockBoundary
= true;
3243 if (!crossedBlockBoundary
&& DidSkip(aIterator
))
3244 crossedBlockBoundary
= true;
3251 nsTextServicesDocument::GetFirstTextNodeInPrevBlock(nsIContent
**aContent
)
3255 NS_ENSURE_TRUE(aContent
, NS_ERROR_NULL_POINTER
);
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
);
3273 if (!mIterator
->IsDone())
3275 nsCOMPtr
<nsIContent
> current
= mIterator
->GetCurrentNode()->IsContent()
3276 ? mIterator
->GetCurrentNode()->AsContent()
3278 current
.forget(aContent
);
3281 // Restore the iterator:
3283 return mIterator
->PositionAt(node
);
3287 nsTextServicesDocument::GetFirstTextNodeInNextBlock(nsIContent
**aContent
)
3291 NS_ENSURE_TRUE(aContent
, NS_ERROR_NULL_POINTER
);
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
);
3309 if (!mIterator
->IsDone())
3311 nsCOMPtr
<nsIContent
> current
= mIterator
->GetCurrentNode()->IsContent()
3312 ? mIterator
->GetCurrentNode()->AsContent()
3314 current
.forget(aContent
);
3317 // Restore the iterator:
3318 return mIterator
->PositionAt(node
);
3322 nsTextServicesDocument::CreateOffsetTable(nsTArray
<OffsetEntry
*> *aOffsetTable
,
3323 nsIContentIterator
*aIterator
,
3324 TSDIteratorStatus
*aIteratorStatus
,
3325 nsIDOMRange
*aIterRange
,
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
);
3340 if (*aIteratorStatus
== nsTextServicesDocument::eIsDone
)
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;
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
);
3369 ClearDidSkip(aIterator
);
3371 while (!aIterator
->IsDone())
3373 nsCOMPtr
<nsIContent
> content
= aIterator
->GetCurrentNode()->IsContent()
3374 ? aIterator
->GetCurrentNode()->AsContent()
3377 if (IsTextNode(content
))
3379 if (!prev
|| HasSameBlockNodeParent(prev
, content
))
3381 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(content
);
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
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
;
3411 if (entry
->mNode
== rngEndNode
)
3413 endOffset
= rngEndOffset
;
3419 entry
->mLength
= endOffset
- startOffset
;
3420 str
= Substring(str
, startOffset
, entry
->mLength
);
3423 offset
+= str
.Length();
3427 // Append the text node's string to the output string:
3445 else if (IsBlockNode(content
))
3450 if (DidSkip(aIterator
))
3456 // Always leave the iterator pointing at the first
3457 // text node of the current block!
3459 aIterator
->PositionAt(first
);
3463 // If we never ran across a text node, the iterator
3464 // might have been pointing to something invalid to
3467 *aIteratorStatus
= nsTextServicesDocument::eIsDone
;
3474 nsTextServicesDocument::RemoveInvalidOffsetEntries()
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.");
3507 nsTextServicesDocument::ClearOffsetTable(nsTArray
<OffsetEntry
*> *aOffsetTable
)
3511 for (i
= 0; i
< aOffsetTable
->Length(); i
++)
3513 delete aOffsetTable
->ElementAt(i
);
3516 aOffsetTable
->Clear();
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
,
3538 if (!mOffsetTable
.InsertElementAt(aTableIndex
+ 1, newEntry
))
3541 return NS_ERROR_FAILURE
;
3544 // Adjust entry fields:
3546 entry
->mLength
= oldLength
;
3547 newEntry
->mNodeOffset
= entry
->mNodeOffset
+ oldLength
;
3553 nsTextServicesDocument::NodeHasOffsetEntry(nsTArray
<OffsetEntry
*> *aOffsetTable
, nsIDOMNode
*aNode
, bool *aHasEntry
, int32_t *aEntryIndex
)
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
)
3581 // Spellchecker code has this. See bug 211343
3582 #define IS_NBSP_CHAR(c) (((unsigned char)0xa0)==(c))
3585 nsTextServicesDocument::FindWordBounds(nsTArray
<OffsetEntry
*> *aOffsetTable
,
3586 nsString
*aBlockStr
,
3588 int32_t aNodeOffset
,
3589 nsIDOMNode
**aWordStartNode
,
3590 int32_t *aWordStartOffset
,
3591 nsIDOMNode
**aWordEndNode
,
3592 int32_t *aWordEndOffset
)
3594 // Initialize return values.
3597 *aWordStartNode
= nullptr;
3598 if (aWordStartOffset
)
3599 *aWordStartOffset
= 0;
3601 *aWordEndNode
= nullptr;
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
])))
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
])))
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
)))
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.
3684 // Check to see if res.mEnd is within the range covered
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.
3699 *aWordEndNode
= entry
->mNode
;
3700 NS_IF_ADDREF(*aWordEndNode
);
3704 *aWordEndOffset
= entry
->mNodeOffset
+ res
.mEnd
- entry
->mStrOffset
;
3715 nsTextServicesDocument::WillInsertNode(nsIDOMNode
*aNode
,
3716 nsIDOMNode
*aParent
,
3723 nsTextServicesDocument::WillDeleteNode(nsIDOMNode
*aChild
)
3729 nsTextServicesDocument::WillSplitNode(nsIDOMNode
*aExistingRightNode
,
3736 nsTextServicesDocument::WillJoinNodes(nsIDOMNode
*aLeftNode
,
3737 nsIDOMNode
*aRightNode
,
3738 nsIDOMNode
*aParent
)
3744 // -------------------------------
3745 // stubs for unused listen methods
3746 // -------------------------------
3749 nsTextServicesDocument::WillCreateNode(const nsAString
& aTag
, nsIDOMNode
*aParent
, int32_t aPosition
)
3755 nsTextServicesDocument::DidCreateNode(const nsAString
& aTag
, nsIDOMNode
*aNode
, nsIDOMNode
*aParent
, int32_t aPosition
, nsresult aResult
)
3761 nsTextServicesDocument::WillInsertText(nsIDOMCharacterData
*aTextNode
, int32_t aOffset
, const nsAString
&aString
)
3767 nsTextServicesDocument::DidInsertText(nsIDOMCharacterData
*aTextNode
, int32_t aOffset
, const nsAString
&aString
, nsresult aResult
)
3773 nsTextServicesDocument::WillDeleteText(nsIDOMCharacterData
*aTextNode
, int32_t aOffset
, int32_t aLength
)
3779 nsTextServicesDocument::DidDeleteText(nsIDOMCharacterData
*aTextNode
, int32_t aOffset
, int32_t aLength
, nsresult aResult
)
3785 nsTextServicesDocument::WillDeleteSelection(nsISelection
*aSelection
)
3791 nsTextServicesDocument::DidDeleteSelection(nsISelection
*aSelection
)