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