editor/txtsvc/src/nsTextServicesDocument.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial