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