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