Coverage Report

Created: 2018-09-25 14:53

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