editor/txtsvc/src/nsTextServicesDocument.cpp

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

mercurial