Back to index

lightning-sunbird  0.9+nobinonly
nsSpellCheckController.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications, Inc.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsSpellCheckController.h"
00040 #include "nsIServiceManager.h"
00041 #include "nsIDOMRange.h"
00042 #include "nsISelection.h"
00043 #include "nsIDOMRange.h"
00044 
00045 #define NBSP_SPACE_CODE ((PRUnichar)32)
00046 
00047 nsSpellCheckController::nsSpellCheckController() :
00048   mOffset(0),
00049   mEndPoint(0)
00050 {
00051 }
00052 
00053 nsSpellCheckController::~nsSpellCheckController()
00054 {
00055 }
00056 
00057 // #define DEBUG_SPELLCHECKGLUE_REFCNT 1
00058 
00059 #ifdef DEBUG_SPELLCHECKGLUE_REFCNT
00060 
00061 nsrefcnt nsSpellCheckController::AddRef(void)
00062 {
00063   return ++mRefCnt;
00064 }
00065 
00066 nsrefcnt nsSpellCheckController::Release(void)
00067 {
00068   NS_PRECONDITION(0 != mRefCnt, "dup release");
00069   if (--mRefCnt == 0) {
00070     NS_DELETEXPCOM(this);
00071     return 0;
00072   }
00073   return mRefCnt;
00074 }
00075 
00076 NS_IMPL_QUERY_INTERFACE1(nsSpellCheckController, nsISpellChecker)
00077 #else
00078 
00079 NS_IMPL_ISUPPORTS1(nsSpellCheckController, nsISpellCheckController)
00080 
00081 #endif
00082 
00083 
00084 nsresult
00085 NS_NewSpellCheckGlue(nsSpellCheckController** result)
00086 {
00087   NS_ENSURE_ARG_POINTER(result);
00088   nsSpellCheckController* spellCheckGlue = new nsSpellCheckController();
00089   NS_ENSURE_TRUE(spellCheckGlue, NS_ERROR_NULL_POINTER);
00090 
00091   *result = spellCheckGlue;
00092   NS_ADDREF(*result);
00093   return NS_OK;
00094 }
00095 
00096 //---------------------------------------------------------------------
00097 // aInitialRange can be NULL
00098 //
00099 NS_IMETHODIMP
00100 nsSpellCheckController::Init(nsITextServicesDocument* aDoc, nsIDOMRange* aInitialRange)
00101 {
00102   NS_ENSURE_ARG_POINTER(aDoc);
00103 
00104   // Create INSO Spell Checker
00105   nsresult rv;
00106   mSpellChecker  = do_GetService(NS_SPELLCHECKER_CONTRACTID, &rv);
00107   NS_ENSURE_SUCCESS(rv, rv);
00108 
00109   // Create WordBreaker
00110   rv = nsSpellCheckUtils::GetWordBreaker(getter_AddRefs(mWordBreaker));
00111   NS_ENSURE_SUCCESS(rv, rv);
00112 
00113   // zero out offset before starting
00114   mOffset = 0;
00115 
00116   return SetDocument(aDoc, aInitialRange);
00117 }
00118 
00119 nsresult
00120 nsSpellCheckController::SetDocument(nsITextServicesDocument *aDoc,nsIDOMRange* aInitialRange)
00121 {
00122   NS_ENSURE_ARG_POINTER(aDoc);
00123 
00124   // XXX: Modify this method so that it can be called
00125   //      more than once with a different aDoc. This will
00126   //      allow us to reuse a spellchecker.
00127 
00128   mDocument = do_QueryInterface(aDoc);
00129   NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
00130 
00131   nsresult rv;
00132   if (!aInitialRange) {
00133     rv = mDocument->FirstBlock();
00134 
00135   } else  {
00136     rv = aInitialRange->GetStartContainer(getter_AddRefs(mStartNode));
00137     NS_ENSURE_SUCCESS(rv, rv);
00138     PRInt32 startOffset;
00139     rv = aInitialRange->GetStartOffset(&startOffset);
00140     NS_ENSURE_SUCCESS(rv, rv);
00141     mOffset = PRUint32(startOffset);
00142 
00143     rv = aInitialRange->GetEndContainer(getter_AddRefs(mEndNode));
00144     NS_ENSURE_SUCCESS(rv, rv);
00145     PRInt32 endOffset;
00146     rv = aInitialRange->GetEndOffset(&endOffset);
00147     NS_ENSURE_SUCCESS(rv, rv);
00148     mEndPoint = PRUint32(endOffset);
00149     
00150     nsITextServicesDocument::TSDBlockSelectionStatus selStatus;
00151     PRInt32 selOffset, selLength;
00152 
00153     rv = mDocument->FirstSelectedBlock(&selStatus, &selOffset, &selLength);
00154   }
00155   NS_ENSURE_SUCCESS(rv, rv);
00156 
00157   rv = nsSpellCheckUtils::LoadTextBlockIntoBuffer(mDocument, mSpellChecker, mBlockBuffer, mText, mOffset);
00158   NS_ENSURE_SUCCESS(rv, rv);
00159 
00160   if (!aInitialRange) {
00161     mOffset = 0;
00162     mEndPoint = mText.Length() - 1;
00163   } else {
00164     rv = FindBeginningOfWord(mOffset);
00165     NS_ENSURE_SUCCESS(rv, rv);
00166     if (mStartNode == mEndNode) {
00167       rv = FindBeginningOfWord(mEndPoint);
00168     }
00169   }
00170 
00171   return rv;
00172 }
00173 
00174 nsresult 
00175 nsSpellCheckController::FindBeginningOfWord(PRUint32& aPos)
00176 {
00177   const PRUnichar* text      = mText.get();
00178   PRUint32         textLen   = mText.Length();
00179   PRUint32         wlen      = 0;
00180   PRUint32         beginWord = 0;
00181   PRUint32         endWord   = 0;
00182 
00183   PRUint32 prvWordEnd;
00184   PRUint32 offset = 0;
00185   while (offset < textLen) {
00186     prvWordEnd = endWord;
00187     nsresult rv = mWordBreaker->FindWord(text, textLen, offset, &beginWord, &endWord);
00188     if (NS_FAILED(rv)) break;
00189 
00190     // The wordBreaker hands back the spaces inbetween the words 
00191     // so we need to skip any words that are all spaces
00192     const PRUnichar* start  = (text+offset);
00193     const PRUnichar* endPtr = (text+endWord);
00194     while (*start == NBSP_SPACE_CODE && start < endPtr) 
00195       start++;
00196 
00197     if (start == endPtr) {
00198       offset = endWord;
00199       continue;
00200     }
00201 
00202     offset = endWord+1;
00203 
00204     wlen = endWord - beginWord;
00205     if (endWord < aPos) {
00206       continue;
00207     }
00208 
00209     if (aPos >= beginWord) {
00210       aPos = beginWord;
00211       break;
00212     }
00213   }
00214 
00215   return NS_OK;
00216 }
00217 
00218 NS_IMETHODIMP
00219 nsSpellCheckController::GetSpellChecker(nsISpellChecker **aSpellChecker)
00220 {
00221   NS_ENSURE_ARG_POINTER(aSpellChecker);
00222   *aSpellChecker = mSpellChecker;
00223   NS_IF_ADDREF(*aSpellChecker);
00224   return NS_OK;
00225 }
00226 
00227 NS_IMETHODIMP
00228 nsSpellCheckController::NextMisspelledWord(PRUnichar **aWord)
00229 {
00230   NS_ENSURE_ARG_POINTER(aWord);
00231   NS_ENSURE_TRUE(mDocument && mSpellChecker && mWordBreaker, NS_ERROR_NULL_POINTER);
00232 
00233   // Init the return values.
00234   *aWord = nsnull;
00235 
00236   nsCOMPtr<nsIDOMNode> currentNode;
00237   mDocument->GetCurrentNode(getter_AddRefs(currentNode));
00238 
00239   // There might not have been any text in the
00240   // document. Just return NS_OK;
00241   if (mText.IsEmpty() || (mOffset >= mEndPoint && currentNode == mEndNode)) {
00242     return NS_OK;
00243   }
00244 
00245   nsresult result;
00246 
00247   // Now get the next misspelled word in the document.
00248   const PRUnichar* text         = mText.get();
00249   PRUint32         textLen      = mText.Length();
00250   PRUint32         wlen         = 0;
00251   PRUint32         beginWord    = 0;
00252   PRUint32         endWord      = 0;
00253 
00254   PRBool isMisspelled = PR_FALSE;
00255   while (!isMisspelled) {
00256     PRUnichar* word;
00257     result = FindNextMisspelledWord(text, textLen, mOffset, wlen, 
00258                                     beginWord, endWord, word, PR_TRUE, isMisspelled);
00259     if (NS_FAILED(result) || (beginWord > mEndPoint && currentNode == mEndNode)) {
00260       if (word) nsMemory::Free(word);
00261       return result;
00262     }
00263 
00264     if (isMisspelled) {
00265       *aWord = word;
00266 
00267     } else {
00268       // No more misspelled words in the current buffer.
00269       // Load another text block into the buffer, try again.
00270 
00271       result = mDocument->NextBlock();
00272       NS_ENSURE_SUCCESS(result, result);
00273 
00274       PRBool isDone;
00275 
00276       result = mDocument->IsDone(&isDone);
00277       NS_ENSURE_SUCCESS(result, result);
00278 
00279       if (isDone) {
00280         // No more blocks to process. We're done, so just
00281         // return OK for the result.
00282         return NS_OK;
00283       }
00284 
00285       result = nsSpellCheckUtils::LoadTextBlockIntoBuffer(mDocument, mSpellChecker, mBlockBuffer, mText, mOffset);
00286       NS_ENSURE_SUCCESS(result, result);
00287 
00288       mDocument->GetCurrentNode(getter_AddRefs(currentNode));
00289       if (currentNode == mEndNode) {
00290         FindBeginningOfWord(mEndPoint);
00291       }
00292       mOffset = 0;
00293       wlen = 0;
00294     }
00295   }
00296 
00297   // We have a misspelled word. Select it in the document,
00298   // and scroll the selection into view.
00299 
00300   // XXX: SetSelection() no longer scrolls the selection
00301   //      into view. Something changed underneath the hood
00302   //      in the presentation shell. Need to fix this!
00303 
00304   result = mDocument->SetSelection(beginWord, wlen);
00305   NS_ENSURE_SUCCESS(result, result);
00306 
00307   result = mDocument->ScrollSelectionIntoView();
00308   NS_ENSURE_SUCCESS(result, result);
00309 
00310   return result;
00311 
00312 }
00313 
00314 NS_IMETHODIMP
00315 nsSpellCheckController::CheckWord(const PRUnichar *aWord, PRBool *aIsMisspelled)
00316 {
00317   NS_ENSURE_ARG_POINTER(aWord);
00318   NS_ENSURE_ARG_POINTER(aIsMisspelled);
00319   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NULL_POINTER);
00320 
00321   // start by assuming everything is spelled correctly.
00322   *aIsMisspelled = PR_FALSE;
00323 
00324   return mSpellChecker->Check(aWord, aIsMisspelled);
00325 }
00326 
00327 NS_IMETHODIMP
00328 nsSpellCheckController::Replace(const PRUnichar *aOldWord, const PRUnichar *aNewWord, PRBool aAllOccurrences)
00329 {
00330   NS_ENSURE_ARG_POINTER(aOldWord);
00331   NS_ENSURE_ARG_POINTER(aNewWord);
00332 
00333   NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
00334 
00335   nsresult result;
00336 
00337   // XXX: To make this method work for a non-modal dialog, we will
00338   //      have to add code that compares the current selection with
00339   //      aOldWord.
00340 
00341   if (aAllOccurrences) {
00342     nsString oldWord(aOldWord);
00343     nsString newWord(aNewWord);
00344     result = ReplaceAll(&oldWord, &newWord);
00345 
00346   } else if (!*aNewWord) {
00347     result = mDocument->DeleteSelection();
00348 
00349   } else {
00350     nsString newWord(aNewWord);
00351     result = mDocument->InsertText(&newWord);
00352   }
00353 
00354   return result;
00355 }
00356 
00357 nsresult
00358 nsSpellCheckController::ReplaceAll(const nsString *aOldWord, const nsString *aNewWord)
00359 {
00360   NS_ENSURE_ARG_POINTER(aOldWord);
00361   NS_ENSURE_ARG_POINTER(aNewWord);
00362   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NULL_POINTER);
00363 
00364   nsresult result;
00365   CharBuffer oldWord;
00366 
00367   nsCOMPtr<nsIUnicodeEncoder> unicodeEncoder;
00368 
00369   nsXPIDLString charSet;
00370   result = mSpellChecker->GetCharset(getter_Copies(charSet));
00371   if (NS_SUCCEEDED(result)) {
00372     nsCOMPtr<nsIUnicodeDecoder> unicodeDecoder;
00373     result = nsSpellCheckUtils::CreateUnicodeConverters(charSet, 
00374                                                         getter_AddRefs(unicodeEncoder), 
00375                                                         getter_AddRefs(unicodeDecoder));
00376     NS_ENSURE_TRUE(unicodeEncoder, NS_ERROR_NULL_POINTER);
00377   }
00378 
00379   result = nsSpellCheckUtils::ReadStringIntoBuffer(unicodeEncoder, aOldWord, &oldWord);
00380   NS_ENSURE_SUCCESS(result, result);
00381 
00382   // Skip the current occurrence of old word. It will be the last
00383   // occurrence we replace so that we can figure out where we were
00384   // before replace all started!
00385 
00386   result = ReplaceAllOccurrences(&oldWord, aNewWord);
00387   NS_ENSURE_SUCCESS(result, result);
00388 
00389   PRInt32 i, numBlocksBefore = 0;
00390   PRBool isDone = PR_FALSE;
00391 
00392   // Count the number of text blocks that came before the current
00393   // one, so that we know when to stop replacing after we hit the
00394   // end of the document and swing around to the beginning of the
00395   // document.
00396 
00397   while (!isDone) {
00398     ++numBlocksBefore;
00399 
00400     result = mDocument->PrevBlock();
00401     NS_ENSURE_SUCCESS(result, result);
00402 
00403     result = mDocument->IsDone(&isDone);
00404     NS_ENSURE_SUCCESS(result, result);
00405   }
00406 
00407   // Now reset the document so that we start replacing all occurrences
00408   // between the current block and the end of the document!
00409 
00410   result = mDocument->FirstBlock();
00411   NS_ENSURE_SUCCESS(result, result);
00412 
00413   i = numBlocksBefore;
00414   while (i-- > 0) {
00415     result = mDocument->NextBlock();
00416     NS_ENSURE_SUCCESS(result, result);
00417   }
00418 
00419   // Now replace all occurrences till we hit the end of the document!
00420 
00421   result = mDocument->IsDone(&isDone);
00422   NS_ENSURE_SUCCESS(result, result);
00423 
00424   while (!isDone) {
00425     result = nsSpellCheckUtils::LoadTextBlockIntoBuffer(mDocument, mSpellChecker, mBlockBuffer, mText, mOffset);
00426     NS_ENSURE_SUCCESS(result, result);
00427 
00428     result = ReplaceAllOccurrences(&oldWord, aNewWord);
00429     NS_ENSURE_SUCCESS(result, result);
00430 
00431     result = mDocument->NextBlock();
00432     NS_ENSURE_SUCCESS(result, result);
00433 
00434     result = mDocument->IsDone(&isDone);
00435     NS_ENSURE_SUCCESS(result, result);
00436   }
00437 
00438   // Now swing around to the beginning of the document, and
00439   // replace all occurrences till we hit the old current block!
00440 
00441   result = mDocument->FirstBlock();
00442   NS_ENSURE_SUCCESS(result, result);
00443 
00444   isDone = PR_FALSE;
00445 
00446   while (numBlocksBefore-- > 0 && !isDone) {
00447     result = nsSpellCheckUtils::LoadTextBlockIntoBuffer(mDocument, mSpellChecker, mBlockBuffer, mText, mOffset);
00448     NS_ENSURE_SUCCESS(result, result);
00449 
00450     result = ReplaceAllOccurrences(&oldWord, aNewWord);
00451     NS_ENSURE_SUCCESS(result, result);
00452 
00453     if (numBlocksBefore > 0) {
00454       result = mDocument->NextBlock();
00455       NS_ENSURE_SUCCESS(result, result);
00456 
00457       // Track isDone because I'm paranoid!
00458 
00459       result = mDocument->IsDone(&isDone);
00460       NS_ENSURE_SUCCESS(result, result);
00461     }
00462   }
00463 
00464   // Now reload the text into the buffer so that the
00465   // spelling checker is reset to look for the next
00466   // misspelled word.
00467 
00468   result = nsSpellCheckUtils::LoadTextBlockIntoBuffer(mDocument, mSpellChecker, mBlockBuffer, mText, mOffset);
00469   NS_ENSURE_SUCCESS(result, result);
00470 
00471   return NS_OK;
00472 }
00473 
00474 nsresult
00475 nsSpellCheckController::ReplaceAllOccurrences(const CharBuffer *aOldWord, const nsString *aNewWord)
00476 {
00477   NS_ENSURE_ARG_POINTER(aOldWord);
00478   NS_ENSURE_ARG_POINTER(aNewWord);
00479   NS_ENSURE_TRUE(mSpellChecker && mWordBreaker, NS_ERROR_NULL_POINTER);
00480 
00481   // Now get the next misspelled word in the document.
00482   const PRUnichar* text         = mText.get();
00483   PRUint32         textLen      = mText.Length();
00484   PRUint32         wlen         = 0;
00485   PRUint32         beginWord    = 0;
00486   PRUint32         endWord      = 0;
00487   PRUint32         offset       = 0;
00488 
00489   PRBool isMisspelled = PR_TRUE;
00490   while (isMisspelled) {
00491     PRUnichar* word;
00492     nsresult result  = FindNextMisspelledWord(text, textLen, offset, wlen, 
00493                                               beginWord, endWord, word, PR_FALSE, isMisspelled);
00494     NS_ENSURE_SUCCESS(result, result);
00495 
00496     if (isMisspelled) {
00497 
00498       mWordBuffer.AssureCapacity(wlen + 1);
00499 
00500       PRUint32 i;
00501       for (i = 0; i < wlen; i++)
00502         mWordBuffer.mData[i] = mBlockBuffer.mData[i + beginWord];
00503 
00504       mWordBuffer.mData[i]    = '\0';
00505       mWordBuffer.mDataLength = wlen;
00506 
00507       // XXX: Need to do a case insensitive comparison, and replace
00508       //      with a word that matches the misspelled word's caps.
00509 
00510       if (aOldWord->mDataLength == mWordBuffer.mDataLength &&
00511           !memcmp(aOldWord->mData, mWordBuffer.mData, aOldWord->mDataLength)) {
00512         // We found an occurrence of old word, so replace it!
00513 
00514         result = mDocument->SetSelection(beginWord, wlen);
00515         NS_ENSURE_SUCCESS(result, result);
00516 
00517         result = mDocument->ScrollSelectionIntoView();
00518         NS_ENSURE_SUCCESS(result, result);
00519 
00520         if (aNewWord->Length() > 0)
00521           result = mDocument->InsertText(aNewWord);
00522         else
00523           result = mDocument->DeleteSelection();
00524 
00525         NS_ENSURE_SUCCESS(result, result);
00526         offset = endWord;
00527       }
00528     }
00529   }
00530 
00531   return NS_OK;
00532 }
00533 
00534 NS_IMETHODIMP    
00535 nsSpellCheckController::SpellCheckDOMRange(nsIDOMRange *aRangeToCheck, nsISelection *aSelectionOfWords)
00536 {
00537   NS_ENSURE_ARG_POINTER(aRangeToCheck);
00538   NS_ENSURE_ARG_POINTER(aSelectionOfWords);
00539 
00540   NS_ENSURE_TRUE(mSpellChecker && mWordBreaker, NS_ERROR_NULL_POINTER);
00541   NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
00542 
00543   mDocument->InitWithRange(aRangeToCheck);
00544   nsSpellCheckUtils::LoadTextBlockIntoBuffer(mDocument, mSpellChecker, mBlockBuffer, mText, mOffset);
00545 
00546   // You may want to clear this externally instead of here
00547   aSelectionOfWords->RemoveAllRanges();
00548 
00549   // Now get the next misspelled word in the document.
00550   const PRUnichar* text         = mText.get();
00551   PRUint32         textLen      = mText.Length();
00552   PRUint32         wlen         = 0;
00553   PRUint32         beginWord    = 0;
00554   PRUint32         endWord      = 0;
00555 
00556   PRBool isMisspelled = PR_TRUE;
00557   while (isMisspelled) {
00558     PRUnichar* word;
00559     nsresult result = FindNextMisspelledWord(text, textLen, mOffset, wlen, 
00560                                              beginWord, endWord, word, PR_FALSE, isMisspelled);
00561     NS_ENSURE_SUCCESS(result, result);
00562 
00563     if (isMisspelled) {// We found an occurrence of old word, so replace it!
00564       nsCOMPtr<nsIDOMRange> range;
00565       result = mDocument->GetDOMRangeFor(beginWord, wlen, getter_AddRefs(range));
00566       NS_ENSURE_SUCCESS(result, result);
00567 
00568       result = aSelectionOfWords->AddRange(range);
00569       NS_ENSURE_SUCCESS(result, result);
00570     }
00571   }
00572 
00573   return NS_OK;
00574 }
00575 
00576 //-------------------------------------------------------------
00577 // Helper Method
00578 nsresult
00579 nsSpellCheckController::FindNextMisspelledWord(const PRUnichar* aText, 
00580                                          const PRUint32&  aTextLen,
00581                                          PRUint32&        aOffset,
00582                                          PRUint32&        aWLen,
00583                                          PRUint32&        aBeginWord,
00584                                          PRUint32&        aEndWord,
00585                                          PRUnichar*&      aWord,
00586                                          PRBool           aReturnWord,
00587                                          PRBool&          aIsMisspelled)
00588 {
00589   aWord = nsnull;
00590   aIsMisspelled = PR_FALSE;
00591 
00592 #ifdef DEBUG_rods
00593   DUMPWORDS(mWordBreaker, aText, aTextLen);
00594 #endif
00595 
00596   while (aOffset < aTextLen) {
00597     nsresult result = mWordBreaker->FindWord(aText, aTextLen, aOffset, &aBeginWord, &aEndWord);
00598     NS_ENSURE_SUCCESS(result, result);
00599     aWLen = aEndWord - aBeginWord;
00600 
00601     // The wordBreaker hands back the spaces inbetween the words 
00602     // so we need to skip any words that are all spaces
00603     const PRUnichar* start  = (aText+aOffset);
00604     const PRUnichar* endPtr = (aText+aEndWord);
00605     while (*start == NBSP_SPACE_CODE && start < endPtr) 
00606       start++;
00607 
00608     if (start == endPtr) {
00609       aOffset = aEndWord;
00610       continue;
00611     }
00612 
00613     PRUnichar* word = nsCRT::strndup(start, aWLen);
00614     aOffset = aEndWord;
00615 
00616     result = mSpellChecker->Check(word, &aIsMisspelled);
00617 #if DEBUG_rods
00618     printf("Word [%s] MSP: %s\n", NS_LossyConvertUCS2toASCII(word).get(), aIsMisspelled ? "YES":"NO");
00619 #endif
00620     if (NS_FAILED(result)) {
00621       nsMemory::Free(word);
00622       return result;
00623     }
00624 
00625     if (aIsMisspelled) {
00626       if (aReturnWord) {
00627         aWord = word;
00628 
00629       } else {
00630         nsMemory::Free(word);
00631       }
00632       return NS_OK;
00633     }
00634     nsMemory::Free(word);
00635   }
00636 
00637   return NS_OK;
00638 }