Back to index

lightning-sunbird  0.9+nobinonly
nsTextFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Robert O'Callahan <roc+moz@cs.cmu.edu>
00024  *   Roger B. Sidje <rbs@maths.uq.edu.au>
00025  *   Pierre Phaneuf <pp@ludusdesign.com>
00026  *   Prabhat Hegde <prabhat.hegde@sun.com>
00027  *   Tomi Leppikangas <tomi.leppikangas@oulu.fi>
00028  *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
00029  *   Daniel Glazman <glazman@netscape.com>
00030  *   Neil Deakin <neil@mozdevgroup.com>
00031  *   Masayuki Nakano <masayuki@d-toybox.com>
00032  *
00033  * Alternatively, the contents of this file may be used under the terms of
00034  * either of the GNU General Public License Version 2 or later (the "GPL"),
00035  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00036  * in which case the provisions of the GPL or the LGPL are applicable instead
00037  * of those above. If you wish to allow use of your version of this file only
00038  * under the terms of either the GPL or the LGPL, and not to allow others to
00039  * use your version of this file under the terms of the MPL, indicate your
00040  * decision by deleting the provisions above and replace them with the notice
00041  * and other provisions required by the GPL or the LGPL. If you do not delete
00042  * the provisions above, a recipient may use your version of this file under
00043  * the terms of any one of the MPL, the GPL or the LGPL.
00044  *
00045  * ***** END LICENSE BLOCK ***** */
00046 #include "nsCOMPtr.h"
00047 #include "nsHTMLParts.h"
00048 #include "nsCRT.h"
00049 #include "nsSplittableFrame.h"
00050 #include "nsLineLayout.h"
00051 #include "nsString.h"
00052 #include "nsUnicharUtils.h"
00053 #include "nsPresContext.h"
00054 #include "nsIContent.h"
00055 #include "nsStyleConsts.h"
00056 #include "nsStyleContext.h"
00057 #include "nsCoord.h"
00058 #include "nsIFontMetrics.h"
00059 #include "nsIRenderingContext.h"
00060 #include "nsIPresShell.h"
00061 #include "nsIView.h"
00062 #include "nsIViewManager.h"
00063 #include "nsITimer.h"
00064 #include "prtime.h"
00065 #include "nsVoidArray.h"
00066 #include "prprf.h"
00067 #include "nsIDOMText.h"
00068 #include "nsIDocument.h"
00069 #include "nsIDeviceContext.h"
00070 #include "nsICaret.h"
00071 #include "nsCSSPseudoElements.h"
00072 #include "nsILineBreaker.h"
00073 #include "nsIWordBreaker.h"
00074 #include "nsCompatibility.h"
00075 #include "nsCSSColorUtils.h"
00076 
00077 #include "nsITextContent.h"
00078 #include "nsTextFragment.h"
00079 #include "nsTextTransformer.h"
00080 #include "nsHTMLAtoms.h"
00081 #include "nsLayoutAtoms.h"
00082 #include "nsIFrameSelection.h"
00083 #include "nsISelection.h"
00084 #include "nsIDOMRange.h"
00085 #include "nsILookAndFeel.h"
00086 #include "nsCSSRendering.h"
00087 #include "nsContentUtils.h"
00088 
00089 #include "nsILineIterator.h"
00090 
00091 #include "nsCompressedCharMap.h"
00092 
00093 #include "nsIServiceManager.h"
00094 #ifdef ACCESSIBILITY
00095 #include "nsIAccessible.h"
00096 #include "nsIAccessibilityService.h"
00097 #endif
00098 #include "nsGUIEvent.h"
00099 #include "nsAutoPtr.h"
00100 #include "nsStyleSet.h"
00101 
00102 #include "nsBidiFrames.h"
00103 #include "nsBidiPresUtils.h"
00104 #include "nsBidiUtils.h"
00105 
00106 #ifdef SUNCTL
00107 #include "nsILE.h"
00108 static NS_DEFINE_CID(kLECID, NS_ULE_CID);
00109 #endif /* SUNCTL */
00110 
00111 #ifdef NS_DEBUG
00112 #undef NOISY_BLINK
00113 #undef DEBUG_WORD_WRAPPING
00114 #undef NOISY_REFLOW
00115 #undef NOISY_TRIM
00116 #else
00117 #undef NOISY_BLINK
00118 #undef DEBUG_WORD_WRAPPING
00119 #undef NOISY_REFLOW
00120 #undef NOISY_TRIM
00121 #endif
00122 
00123 // #define DEBUGWORDJUMP
00124 
00125 #define kSZLIG 0x00DF
00126 //----------------------------------------------------------------------
00127 
00128 #define TEXT_BUF_SIZE 100
00129 
00130 //----------------------------------------
00131 
00132 // checks to see if the text can be lightened..
00133 // text is darkend
00134 inline PRBool CanDarken(nsPresContext* aPresContext)
00135 {
00136   PRBool darken;
00137 
00138   if (aPresContext->GetBackgroundColorDraw()) {
00139     darken = PR_FALSE;
00140   } else {
00141     if (aPresContext->GetBackgroundImageDraw()) {
00142       darken = PR_FALSE;
00143     } else {
00144       darken = PR_TRUE;
00145     }
00146   }
00147 
00148   return darken;
00149 }
00150 
00151 
00152 struct nsAutoIndexBuffer {
00153   nsAutoIndexBuffer();
00154   ~nsAutoIndexBuffer();
00155 
00156   nsresult GrowTo(PRInt32 aAtLeast);
00157 
00158   PRInt32* mBuffer;
00159   PRInt32 mBufferLen;
00160   PRInt32 mAutoBuffer[TEXT_BUF_SIZE];
00161 };
00162 
00163 nsAutoIndexBuffer::nsAutoIndexBuffer()
00164   : mBuffer(mAutoBuffer),
00165     mBufferLen(TEXT_BUF_SIZE)
00166 {
00167 #ifdef DEBUG
00168   memset(mAutoBuffer, 0xdd, sizeof(mAutoBuffer));
00169 #endif 
00170 }
00171 
00172 nsAutoIndexBuffer::~nsAutoIndexBuffer()
00173 {
00174   if (mBuffer && (mBuffer != mAutoBuffer)) {
00175     delete [] mBuffer;
00176   }
00177 }
00178 
00179 nsresult
00180 nsAutoIndexBuffer::GrowTo(PRInt32 aAtLeast)
00181 {
00182   if (aAtLeast > mBufferLen)
00183   {
00184     PRInt32 newSize = mBufferLen * 2;
00185     if (newSize < mBufferLen + aAtLeast) {
00186       newSize = mBufferLen * 2 + aAtLeast;
00187     }
00188     PRInt32* newBuffer = new PRInt32[newSize];
00189     if (!newBuffer) {
00190       return NS_ERROR_OUT_OF_MEMORY;
00191     }
00192 #ifdef DEBUG
00193     memset(newBuffer, 0xdd, sizeof(PRInt32) * newSize);
00194 #endif
00195     memcpy(newBuffer, mBuffer, sizeof(PRInt32) * mBufferLen);
00196     if (mBuffer != mAutoBuffer) {
00197       delete [] mBuffer;
00198     }
00199     mBuffer = newBuffer;
00200     mBufferLen = newSize;
00201   }
00202   return NS_OK;
00203 }
00204 
00205 struct nsAutoPRUint8Buffer {
00206   nsAutoPRUint8Buffer();
00207   ~nsAutoPRUint8Buffer();
00208 
00209   nsresult GrowTo(PRInt32 aAtLeast);
00210 
00211   PRUint8* mBuffer;
00212   PRInt32 mBufferLen;
00213   PRUint8 mAutoBuffer[TEXT_BUF_SIZE];
00214 };
00215 
00216 nsAutoPRUint8Buffer::nsAutoPRUint8Buffer()
00217   : mBuffer(mAutoBuffer),
00218     mBufferLen(TEXT_BUF_SIZE)
00219 {
00220 #ifdef DEBUG
00221   memset(mAutoBuffer, 0xdd, sizeof(mAutoBuffer));
00222 #endif 
00223 }
00224 
00225 nsAutoPRUint8Buffer::~nsAutoPRUint8Buffer()
00226 {
00227   if (mBuffer && (mBuffer != mAutoBuffer)) {
00228     delete [] mBuffer;
00229   }
00230 }
00231 
00232 nsresult
00233 nsAutoPRUint8Buffer::GrowTo(PRInt32 aAtLeast)
00234 {
00235   if (aAtLeast > mBufferLen)
00236   {
00237     PRInt32 newSize = mBufferLen * 2;
00238     if (newSize < mBufferLen + aAtLeast) {
00239       newSize = mBufferLen * 2 + aAtLeast;
00240     }
00241     PRUint8* newBuffer = new PRUint8[newSize];
00242     if (!newBuffer) {
00243       return NS_ERROR_OUT_OF_MEMORY;
00244     }
00245 #ifdef DEBUG
00246     memset(newBuffer, 0xdd, sizeof(PRUint8) * newSize);
00247 #endif
00248     memcpy(newBuffer, mBuffer, sizeof(PRUint8) * mBufferLen);
00249     if (mBuffer != mAutoBuffer) {
00250       delete [] mBuffer;
00251     }
00252     mBuffer = newBuffer;
00253     mBufferLen = newSize;
00254   }
00255   return NS_OK;
00256 }
00257 
00258 
00259 //----------------------------------------------------------------------
00260 
00261 // Helper class for managing blinking text
00262 
00263 class nsBlinkTimer : public nsITimerCallback
00264 {
00265 public:
00266   nsBlinkTimer();
00267   virtual ~nsBlinkTimer();
00268 
00269   NS_DECL_ISUPPORTS
00270 
00271   void AddFrame(nsPresContext* aPresContext, nsIFrame* aFrame);
00272 
00273   PRBool RemoveFrame(nsIFrame* aFrame);
00274 
00275   PRInt32 FrameCount();
00276 
00277   void Start();
00278 
00279   void Stop();
00280 
00281   NS_DECL_NSITIMERCALLBACK
00282 
00283   static nsresult AddBlinkFrame(nsPresContext* aPresContext, nsIFrame* aFrame);
00284   static nsresult RemoveBlinkFrame(nsIFrame* aFrame);
00285   
00286   static PRBool   GetBlinkIsOff() { return sState == 3; }
00287   
00288 protected:
00289 
00290   struct FrameData {
00291     nsPresContext* mPresContext;  // pres context associated with the frame
00292     nsIFrame*       mFrame;
00293 
00294 
00295     FrameData(nsPresContext* aPresContext,
00296               nsIFrame*       aFrame)
00297       : mPresContext(aPresContext), mFrame(aFrame) {}
00298   };
00299 
00300   nsCOMPtr<nsITimer> mTimer;
00301   nsVoidArray     mFrames;
00302   nsPresContext* mPresContext;
00303 
00304 protected:
00305 
00306   static nsBlinkTimer* sTextBlinker;
00307   static PRUint32      sState; // 0-2 == on; 3 == off
00308   
00309 };
00310 
00311 nsBlinkTimer* nsBlinkTimer::sTextBlinker = nsnull;
00312 PRUint32      nsBlinkTimer::sState = 0;
00313 
00314 #ifdef NOISY_BLINK
00315 static PRTime gLastTick;
00316 #endif
00317 
00318 nsBlinkTimer::nsBlinkTimer()
00319 {
00320 }
00321 
00322 nsBlinkTimer::~nsBlinkTimer()
00323 {
00324   Stop();
00325   sTextBlinker = nsnull;
00326 }
00327 
00328 void nsBlinkTimer::Start()
00329 {
00330   nsresult rv;
00331   mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
00332   if (NS_OK == rv) {
00333     mTimer->InitWithCallback(this, 250, nsITimer::TYPE_REPEATING_PRECISE);
00334   }
00335 }
00336 
00337 void nsBlinkTimer::Stop()
00338 {
00339   if (nsnull != mTimer) {
00340     mTimer->Cancel();
00341   }
00342 }
00343 
00344 NS_IMPL_ISUPPORTS1(nsBlinkTimer, nsITimerCallback)
00345 
00346 void nsBlinkTimer::AddFrame(nsPresContext* aPresContext, nsIFrame* aFrame) {
00347   FrameData* frameData = new FrameData(aPresContext, aFrame);
00348   mFrames.AppendElement(frameData);
00349   if (1 == mFrames.Count()) {
00350     Start();
00351   }
00352 }
00353 
00354 PRBool nsBlinkTimer::RemoveFrame(nsIFrame* aFrame) {
00355   PRInt32 i, n = mFrames.Count();
00356   PRBool rv = PR_FALSE;
00357   for (i = 0; i < n; i++) {
00358     FrameData* frameData = (FrameData*) mFrames.ElementAt(i);
00359 
00360     if (frameData->mFrame == aFrame) {
00361       rv = mFrames.RemoveElementAt(i);
00362       delete frameData;
00363       break;
00364     }
00365   }
00366   
00367   if (0 == mFrames.Count()) {
00368     Stop();
00369   }
00370   return rv;
00371 }
00372 
00373 PRInt32 nsBlinkTimer::FrameCount() {
00374   return mFrames.Count();
00375 }
00376 
00377 NS_IMETHODIMP nsBlinkTimer::Notify(nsITimer *timer)
00378 {
00379   // Toggle blink state bit so that text code knows whether or not to
00380   // render. All text code shares the same flag so that they all blink
00381   // in unison.
00382   sState = (sState + 1) % 4;
00383   if (sState == 1 || sState == 2)
00384     // States 0, 1, and 2 are all the same.
00385     return NS_OK;
00386 
00387 #ifdef NOISY_BLINK
00388   PRTime now = PR_Now();
00389   char buf[50];
00390   PRTime delta;
00391   LL_SUB(delta, now, gLastTick);
00392   gLastTick = now;
00393   PR_snprintf(buf, sizeof(buf), "%lldusec", delta);
00394   printf("%s\n", buf);
00395 #endif
00396 
00397   PRInt32 i, n = mFrames.Count();
00398   for (i = 0; i < n; i++) {
00399     FrameData* frameData = (FrameData*) mFrames.ElementAt(i);
00400 
00401     // Determine damaged area and tell view manager to redraw it
00402     // blink doesn't blink outline ... I hope
00403     nsRect bounds(nsPoint(0, 0), frameData->mFrame->GetSize());
00404     frameData->mFrame->Invalidate(bounds, PR_FALSE);
00405   }
00406   return NS_OK;
00407 }
00408 
00409 
00410 // static
00411 nsresult nsBlinkTimer::AddBlinkFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
00412 {
00413   if (!sTextBlinker)
00414   {
00415     sTextBlinker = new nsBlinkTimer;
00416     if (!sTextBlinker) return NS_ERROR_OUT_OF_MEMORY;
00417   }
00418   
00419   NS_ADDREF(sTextBlinker);
00420 
00421   sTextBlinker->AddFrame(aPresContext, aFrame);
00422   return NS_OK;
00423 }
00424 
00425 
00426 // static
00427 nsresult nsBlinkTimer::RemoveBlinkFrame(nsIFrame* aFrame)
00428 {
00429   NS_ASSERTION(sTextBlinker, "Should have blink timer here");
00430   
00431   nsBlinkTimer* blinkTimer = sTextBlinker;    // copy so we can call NS_RELEASE on it
00432   if (!blinkTimer) return NS_OK;
00433   
00434   blinkTimer->RemoveFrame(aFrame);  
00435   NS_RELEASE(blinkTimer);
00436   
00437   return NS_OK;
00438 }
00439 
00440 //----------------------------------------------------------------------
00441 
00442 class nsTextFrame : public nsFrame {
00443 public:
00444   nsTextFrame();
00445 
00446   // nsIFrame
00447   NS_IMETHOD Paint(nsPresContext*      aPresContext,
00448                    nsIRenderingContext& aRenderingContext,
00449                    const                nsRect& aDirtyRect,
00450                    nsFramePaintLayer    aWhichLayer,
00451                    PRUint32             aFlags);
00452 
00453   NS_IMETHOD Destroy(nsPresContext* aPresContext);
00454 
00455   NS_IMETHOD GetCursor(const nsPoint& aPoint,
00456                        nsIFrame::Cursor& aCursor);
00457 
00458   NS_IMETHOD CharacterDataChanged(nsPresContext* aPresContext,
00459                                   nsIContent*     aChild,
00460                                   PRBool          aAppend);
00461 
00462   virtual nsIFrame* GetNextInFlow() const {
00463     return mNextInFlow;
00464   }
00465   NS_IMETHOD SetNextInFlow(nsIFrame* aNextInFlow) {
00466     mNextInFlow = aNextInFlow;
00467     return NS_OK;
00468   }
00469   virtual nsIFrame* GetLastInFlow() const;
00470   
00471   NS_IMETHOD  IsSplittable(nsSplittableType& aIsSplittable) const {
00472     aIsSplittable = NS_FRAME_SPLITTABLE;
00473     return NS_OK;
00474   }
00475 
00481   virtual nsIAtom* GetType() const;
00482   
00483 #ifdef DEBUG
00484   NS_IMETHOD List(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent) const;
00485   NS_IMETHOD GetFrameName(nsAString& aResult) const;
00486   NS_IMETHOD_(nsFrameState) GetDebugStateBits() const ;
00487 #endif
00488 
00489   NS_IMETHOD GetPosition(nsPresContext*  aPresContext,
00490                          const nsPoint&  aPoint,
00491                          nsIContent **   aNewContent,
00492                          PRInt32&        aContentOffset,
00493                          PRInt32&        aContentOffsetEnd);
00494 
00495   NS_IMETHOD GetContentAndOffsetsFromPoint(nsPresContext* aPresContext,
00496                          const nsPoint&  aPoint,
00497                          nsIContent **   aNewContent,
00498                          PRInt32&        aContentOffset,
00499                          PRInt32&        aContentOffsetEnd,
00500                          PRBool&         aBeginFrameContent);
00501 
00502   NS_IMETHOD GetPositionSlowly(nsPresContext*  aPresContext,
00503                          nsIRenderingContext * aRendContext,
00504                          const nsPoint&        aPoint,
00505                          nsIContent **         aNewContent,
00506                          PRInt32&              aOffset);
00507 
00508 
00509   NS_IMETHOD SetSelected(nsPresContext* aPresContext,
00510                          nsIDOMRange *aRange,
00511                          PRBool aSelected,
00512                          nsSpread aSpread);
00513 
00514   NS_IMETHOD PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos);
00515   NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval);
00516 
00517   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
00518                          nsGUIEvent *    aEvent,
00519                          nsEventStatus*  aEventStatus);
00520 
00521   NS_IMETHOD GetOffsets(PRInt32 &start, PRInt32 &end)const;
00522 
00523   virtual void AdjustOffsetsForBidi(PRInt32 start, PRInt32 end);
00524   
00525   NS_IMETHOD GetPointFromOffset(nsPresContext*         inPresContext,
00526                                 nsIRenderingContext*    inRendContext,
00527                                 PRInt32                 inOffset,
00528                                 nsPoint*                outPoint);
00529                                 
00530   NS_IMETHOD  GetChildFrameContainingOffset(PRInt32     inContentOffset,
00531                                 PRBool                  inHint,
00532                                 PRInt32*                outFrameContentOffset,
00533                                 nsIFrame*               *outChildFrame);
00534 
00535   NS_IMETHOD IsVisibleForPainting(nsPresContext *     aPresContext, 
00536                                   nsIRenderingContext& aRenderingContext,
00537                                   PRBool               aCheckVis,
00538                                   PRBool*              aIsVisible);
00539 
00540   virtual PRBool IsEmpty();
00541   virtual PRBool IsSelfEmpty() { return IsEmpty(); }
00542 
00543 #ifdef ACCESSIBILITY
00544   NS_IMETHOD GetAccessible(nsIAccessible** aAccessible);
00545 #endif
00546 
00547   // nsIHTMLReflow
00548   NS_IMETHOD Reflow(nsPresContext* aPresContext,
00549                     nsHTMLReflowMetrics& aMetrics,
00550                     const nsHTMLReflowState& aReflowState,
00551                     nsReflowStatus& aStatus);
00552   NS_IMETHOD CanContinueTextRun(PRBool& aContinueTextRun) const;
00553   NS_IMETHOD AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace);
00554   NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext,
00555                                     nsIRenderingContext& aRC,
00556                                     nscoord& aDeltaWidth,
00557                                     PRBool& aLastCharIsJustifiable);
00558 
00559   struct TextStyle {
00560     const nsStyleFont* mFont;
00561     const nsStyleText* mText;
00562     nsIFontMetrics* mNormalFont;
00563     nsIFontMetrics* mSmallFont;
00564     nsIFontMetrics* mLastFont;
00565     PRBool mSmallCaps;
00566     nscoord mWordSpacing;
00567     nscoord mLetterSpacing;
00568     nscoord mSpaceWidth;
00569     nscoord mAveCharWidth;
00570     PRBool mJustifying;
00571     PRBool mPreformatted;
00572     PRInt32 mNumJustifiableCharacterToRender;
00573     PRInt32 mNumJustifiableCharacterToMeasure;
00574     nscoord mExtraSpacePerJustifiableCharacter;
00575     PRInt32 mNumJustifiableCharacterReceivingExtraJot;
00576 
00577     TextStyle(nsPresContext* aPresContext,
00578               nsIRenderingContext& aRenderingContext,
00579               nsStyleContext* sc)
00580     {
00581       // Get style data
00582       mFont = sc->GetStyleFont();
00583       mText = sc->GetStyleText();
00584 
00585       // Cache the original decorations and reuse the current font
00586       // to query metrics, rather than creating a new font which is expensive.
00587       nsFont* plainFont = (nsFont *)&mFont->mFont; //XXX: Change to use a CONST_CAST macro.
00588       NS_ASSERTION(plainFont, "null plainFont: font problems in TextStyle::TextStyle");
00589       PRUint8 originalDecorations = plainFont->decorations;
00590       plainFont->decorations = NS_FONT_DECORATION_NONE;
00591       mAveCharWidth = 0;
00592       SetFontFromStyle(&aRenderingContext, sc); // some users of the struct expect this state
00593       aRenderingContext.GetFontMetrics(mNormalFont);
00594       mNormalFont->GetSpaceWidth(mSpaceWidth);
00595       mNormalFont->GetAveCharWidth(mAveCharWidth);
00596       mLastFont = mNormalFont;
00597 
00598       // Get the small-caps font if needed
00599       mSmallCaps = NS_STYLE_FONT_VARIANT_SMALL_CAPS == plainFont->variant;
00600       if (mSmallCaps) {
00601         nscoord originalSize = plainFont->size;
00602         plainFont->size = nscoord(0.8 * plainFont->size);
00603         mSmallFont = aPresContext->GetMetricsFor(*plainFont).get();  // addrefs
00604         // Reset to the size value saved earlier.
00605         plainFont->size = originalSize;
00606       }
00607       else {
00608         mSmallFont = nsnull;
00609       }
00610 
00611       // Reset to the decoration saved earlier
00612       plainFont->decorations = originalDecorations; 
00613 
00614       // Get the word and letter spacing
00615       PRIntn unit = mText->mWordSpacing.GetUnit();
00616       if (eStyleUnit_Coord == unit) {
00617         mWordSpacing = mText->mWordSpacing.GetCoordValue();
00618       } else {
00619         mWordSpacing = 0;
00620       }
00621 
00622       unit = mText->mLetterSpacing.GetUnit();
00623       if (eStyleUnit_Coord == unit) {
00624         mLetterSpacing = mText->mLetterSpacing.GetCoordValue();
00625       } else {
00626         mLetterSpacing = 0;
00627       }
00628 
00629       mNumJustifiableCharacterToRender = 0;
00630       mNumJustifiableCharacterToMeasure = 0;
00631       mNumJustifiableCharacterReceivingExtraJot = 0;
00632       mExtraSpacePerJustifiableCharacter = 0;
00633       mPreformatted = (NS_STYLE_WHITESPACE_PRE == mText->mWhiteSpace) ||
00634         (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == mText->mWhiteSpace);
00635 
00636       mJustifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == mText->mTextAlign) &&
00637         !mPreformatted;
00638     }
00639 
00640     ~TextStyle() {
00641       NS_IF_RELEASE(mNormalFont);
00642       NS_IF_RELEASE(mSmallFont);
00643     }
00644   };
00645 
00646   // Contains extra style data needed only for painting (not reflowing)
00647   struct TextPaintStyle : TextStyle {
00648     const nsStyleColor* mColor;
00649     nscolor mSelectionTextColor;
00650     nscolor mSelectionBGColor;
00651 
00652     TextPaintStyle(nsPresContext* aPresContext,
00653                    nsIRenderingContext& aRenderingContext,
00654                    nsStyleContext* sc)
00655       : TextStyle(aPresContext, aRenderingContext, sc)
00656     {
00657       mColor = sc->GetStyleColor();
00658 
00659       // Get colors from look&feel
00660       mSelectionBGColor = NS_RGB(0, 0, 0);
00661       mSelectionTextColor = NS_RGB(255, 255, 255);
00662       nsILookAndFeel* look = aPresContext->LookAndFeel();
00663       look->GetColor(nsILookAndFeel::eColor_TextSelectBackground,
00664                      mSelectionBGColor);
00665       look->GetColor(nsILookAndFeel::eColor_TextSelectForeground,
00666                      mSelectionTextColor);
00667     }
00668 
00669     ~TextPaintStyle() {
00670       mColor = nsnull;
00671     }
00672   };
00673 
00674   struct TextReflowData {
00675     PRInt32             mX;                   // OUT
00676     PRInt32             mOffset;              // IN/OUT How far along we are in the content
00677     nscoord             mMaxWordWidth;        // OUT
00678     nscoord             mAscent;              // OUT
00679     nscoord             mDescent;             // OUT
00680     PRPackedBool        mWrapping;            // IN
00681     PRPackedBool        mSkipWhitespace;      // IN
00682     PRPackedBool        mMeasureText;         // IN
00683     PRPackedBool        mInWord;              // IN
00684     PRPackedBool        mFirstLetterOK;       // IN
00685     PRPackedBool        mCanBreakBefore;         // IN
00686     PRPackedBool        mComputeMaxWordWidth; // IN
00687     PRPackedBool        mTrailingSpaceTrimmed; // IN/OUT
00688   
00689     TextReflowData(PRInt32 aStartingOffset,
00690                    PRBool  aWrapping,
00691                    PRBool  aSkipWhitespace,
00692                    PRBool  aMeasureText,
00693                    PRBool  aInWord,
00694                    PRBool  aFirstLetterOK,
00695                    PRBool  aCanBreakBefore,
00696                    PRBool  aComputeMaxWordWidth,
00697                    PRBool  aTrailingSpaceTrimmed)
00698       : mX(0),
00699         mOffset(aStartingOffset),
00700         mMaxWordWidth(0),
00701         mAscent(0),
00702         mDescent(0),
00703         mWrapping(aWrapping),
00704         mSkipWhitespace(aSkipWhitespace),
00705         mMeasureText(aMeasureText),
00706         mInWord(aInWord),
00707         mFirstLetterOK(aFirstLetterOK),
00708         mCanBreakBefore(aCanBreakBefore),
00709         mComputeMaxWordWidth(aComputeMaxWordWidth),
00710         mTrailingSpaceTrimmed(aTrailingSpaceTrimmed)
00711     {}
00712   };
00713 
00714   nsIDocument* GetDocument(nsPresContext* aPresContext);
00715 
00716   void PrepareUnicodeText(nsTextTransformer& aTransformer,
00717                           nsAutoIndexBuffer* aIndexBuffer,
00718                           nsAutoTextBuffer* aTextBuffer,
00719                           PRInt32* aTextLen,
00720                           PRBool aForceArabicShaping = PR_FALSE,
00721                           PRIntn* aJustifiableCharCount = nsnull);
00722   void ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext,
00723                                         TextStyle& aTextStyle,
00724                                         PRUnichar* aBuffer, PRInt32 aLength, PRInt32 aNumJustifiableCharacter);
00725  
00730   void PaintTextDecorations(nsIRenderingContext& aRenderingContext,
00731                             nsStyleContext* aStyleContext,
00732                             nsPresContext* aPresContext,
00733                             TextPaintStyle& aStyle,
00734                             nscoord aX, nscoord aY, nscoord aWidth,
00735                             PRBool aRightToLeftText,
00736                             PRUnichar* aText = nsnull,
00737                             SelectionDetails *aDetails = nsnull,
00738                             PRUint32 aIndex = 0,
00739                             PRUint32 aLength = 0,
00740                             const nscoord* aSpacing = nsnull);
00741 
00742   void PaintTextSlowly(nsPresContext* aPresContext,
00743                        nsIRenderingContext& aRenderingContext,
00744                        nsStyleContext* aStyleContext,
00745                        TextPaintStyle& aStyle,
00746                        nscoord aX, nscoord aY);
00747 
00748   // The passed-in rendering context must have its color set to the color the
00749   // text should be rendered in.
00754   void RenderString(nsIRenderingContext& aRenderingContext,
00755                     nsStyleContext* aStyleContext,
00756                     nsPresContext* aPresContext,
00757                     TextPaintStyle& aStyle,
00758                     PRBool aRightToLeftText,
00759                     PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
00760                     nscoord aX, nscoord aY,
00761                     nscoord aWidth,
00762                     SelectionDetails *aDetails = nsnull);
00763 
00764   void MeasureSmallCapsText(const nsHTMLReflowState& aReflowState,
00765                             TextStyle& aStyle,
00766                             PRUnichar* aWord,
00767                             PRInt32 aWordLength,
00768                             PRBool aIsEndOfFrame,
00769                             nsTextDimensions* aDimensionsResult);
00770 
00771   PRUint32 EstimateNumChars(PRUint32 aAvailableWidth,
00772                             PRUint32 aAverageCharWidth);
00773 
00774   nsReflowStatus MeasureText(nsPresContext*          aPresContext,
00775                              const nsHTMLReflowState& aReflowState,
00776                              nsTextTransformer&       aTx,
00777                              nsILineBreaker*          aLb,
00778                              TextStyle&               aTs,
00779                              TextReflowData&          aTextData);
00780   
00781   void GetTextDimensions(nsIRenderingContext& aRenderingContext,
00782                 TextStyle& aStyle,
00783                 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
00784                 nsTextDimensions* aDimensionsResult);
00785 
00786   //this returns the index into the PAINTBUFFER of the x coord aWidth(based on 0 as far left) 
00787   //also note: this is NOT added to mContentOffset since that would imply that this return is
00788   //meaningful to content yet. use index buffer from prepareunicodestring to find the content offset.
00789   PRInt32 GetLengthSlowly(nsIRenderingContext& aRenderingContext,
00790                 TextStyle& aStyle,
00791                 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
00792                 nscoord aWidth);
00793 
00794   PRBool IsTextInSelection(nsPresContext* aPresContext,
00795                            nsIRenderingContext& aRenderingContext);
00796 
00797   nsresult GetTextInfoForPainting(nsPresContext*          aPresContext,
00798                                   nsIRenderingContext&     aRenderingContext,
00799                                   nsIPresShell**           aPresShell,
00800                                   nsISelectionController** aSelectionController,
00801                                   PRBool&                  aDisplayingSelection,
00802                                   PRBool&                  aIsPaginated,
00803                                   PRBool&                  aIsSelected,
00804                                   PRBool&                  aHideStandardSelection,
00805                                   PRInt16&                 aSelectionValue,
00806                                   nsILineBreaker**         aLineBreaker);
00807 
00808   void PaintUnicodeText(nsPresContext* aPresContext,
00809                         nsIRenderingContext& aRenderingContext,
00810                         nsStyleContext* aStyleContext,
00811                         TextPaintStyle& aStyle,
00812                         nscoord dx, nscoord dy);
00813 
00814   void PaintAsciiText(nsPresContext* aPresContext,
00815                       nsIRenderingContext& aRenderingContext,
00816                       nsStyleContext* aStyleContext,
00817                       TextPaintStyle& aStyle,
00818                       nscoord dx, nscoord dy);
00819 
00837   nsTextDimensions ComputeTotalWordDimensions(nsPresContext* aPresContext,
00838                                 nsILineBreaker* aLineBreaker,
00839                                 nsLineLayout& aLineLayout,
00840                                 const nsHTMLReflowState& aReflowState,
00841                                 nsIFrame* aNextFrame,
00842                                 const nsTextDimensions& aBaseDimensions,
00843                                 PRUnichar* aWordBuf,
00844                                 PRUint32   aWordBufLen,
00845                                 PRUint32   aWordBufSize,
00846                                 PRBool     aCanBreakBefore);
00847 
00861   nsTextDimensions ComputeWordFragmentDimensions(nsPresContext* aPresContext,
00862                                    nsILineBreaker* aLineBreaker,
00863                                    nsLineLayout& aLineLayout,
00864                                    const nsHTMLReflowState& aReflowState,
00865                                    nsIFrame* aNextFrame,
00866                                    nsIContent* aContent,
00867                                    nsITextContent* aText,
00868                                    PRInt32* aMoreSize,
00869                                    const PRUnichar* aWordBuf,
00870                                    PRUint32 &aWordBufLen,
00871                                    PRUint32 aWordBufSize,
00872                                    PRBool aCanBreakBefore);
00873 
00874 #ifdef DEBUG
00875   void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const;
00876 #endif
00877 
00878 protected:
00879   virtual ~nsTextFrame();
00880 
00881   nsIFrame* mNextInFlow;
00882   PRInt32   mContentOffset;
00883   PRInt32   mContentLength;
00884   PRInt32   mColumn;
00885   nscoord   mAscent;
00886   //factored out method for GetTextDimensions and getlengthslowly. if aGetTextDimensions is non-zero number then measure to the width field and return the length. else shove total dimensions into result
00887   PRInt32 GetTextDimensionsOrLength(nsIRenderingContext& aRenderingContext,
00888                 TextStyle& aStyle,
00889                 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
00890                 nsTextDimensions* aDimensionsResult,
00891                 PRBool aGetTextDimensions/* true=get dimensions false = return length up to aDimensionsResult->width size*/);
00892   nsresult GetContentAndOffsetsForSelection(nsPresContext*  aPresContext,nsIContent **aContent, PRInt32 *aOffset, PRInt32 *aLength);
00893 
00894   void AdjustSelectionPointsForBidi(SelectionDetails *sdptr,
00895                                     PRInt32 textLength,
00896                                     PRBool isRTLChars,
00897                                     PRBool isOddLevel,
00898                                     PRBool isBidiSystem);
00899 
00900   void SetOffsets(PRInt32 start, PRInt32 end);
00901 
00902   PRBool IsChineseJapaneseLangGroup();
00903   PRBool IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ);
00904 
00905   nsresult FillClusterBuffer(nsPresContext *aPresContext, const PRUnichar *aText,
00906                              PRUint32 aLength, nsAutoPRUint8Buffer& aClusterBuffer);
00907 };
00908 
00909 #ifdef ACCESSIBILITY
00910 NS_IMETHODIMP nsTextFrame::GetAccessible(nsIAccessible** aAccessible)
00911 {
00912   if (mRect.width > 0 || mRect.height > 0 || GetNextInFlow()) {
00913 
00914     nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
00915 
00916     if (accService) {
00917       return accService->CreateHTMLTextAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible);
00918     }
00919   }
00920   return NS_ERROR_FAILURE;
00921 }
00922 #endif
00923 
00924 
00925 //-----------------------------------------------------------------------------
00926 NS_IMETHODIMP
00927 nsTextFrame::Destroy(nsPresContext* aPresContext)
00928 {
00929   if (mNextInFlow) {
00930     mNextInFlow->SetPrevInFlow(nsnull);
00931   }
00932   // Let the base class destroy the frame
00933   return nsFrame::Destroy(aPresContext);
00934 }
00935 
00936 class nsContinuingTextFrame : public nsTextFrame {
00937 public:
00938   NS_IMETHOD Init(nsPresContext*  aPresContext,
00939                   nsIContent*      aContent,
00940                   nsIFrame*        aParent,
00941                   nsStyleContext*  aContext,
00942                   nsIFrame*        aPrevInFlow);
00943 
00944   NS_IMETHOD Destroy(nsPresContext* aPresContext);
00945 
00946   virtual nsIFrame* GetPrevInFlow() const {
00947     return mPrevInFlow;
00948   }
00949   NS_IMETHOD SetPrevInFlow(nsIFrame* aPrevInFlow) {
00950     mPrevInFlow = aPrevInFlow;
00951     return NS_OK;
00952   }
00953   virtual nsIFrame* GetFirstInFlow() const;
00954   
00955 protected:
00956   nsIFrame* mPrevInFlow;
00957 };
00958 
00959 NS_IMETHODIMP
00960 nsContinuingTextFrame::Init(nsPresContext*  aPresContext,
00961                             nsIContent*      aContent,
00962                             nsIFrame*        aParent,
00963                             nsStyleContext*  aContext,
00964                             nsIFrame*        aPrevInFlow)
00965 {
00966   NS_PRECONDITION(aContent->IsContentOfType(nsIContent::eTEXT),
00967                   "Bogus content!");
00968 
00969   nsresult  rv;
00970   
00971   rv = nsTextFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00972 
00973   if (aPrevInFlow) {
00974     // Hook the frame into the flow
00975     mPrevInFlow = aPrevInFlow;
00976     aPrevInFlow->SetNextInFlow(this);
00977 #ifdef IBMBIDI
00978     if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
00979       PRInt32 start, end;
00980       aPrevInFlow->GetOffsets(start, mContentOffset);
00981 
00982       nsPropertyTable *propTable = aPresContext->PropertyTable();
00983       propTable->SetProperty(this, nsLayoutAtoms::embeddingLevel,
00984             propTable->GetProperty(aPrevInFlow, nsLayoutAtoms::embeddingLevel),
00985                              nsnull, nsnull);
00986       propTable->SetProperty(this, nsLayoutAtoms::baseLevel,
00987                 propTable->GetProperty(aPrevInFlow, nsLayoutAtoms::baseLevel),
00988                              nsnull, nsnull);
00989       propTable->SetProperty(this, nsLayoutAtoms::charType,
00990                  propTable->GetProperty(aPrevInFlow, nsLayoutAtoms::charType),
00991                              nsnull, nsnull);
00992 
00993       void* value = propTable->GetProperty(aPrevInFlow,
00994                                            nsLayoutAtoms::nextBidi);
00995       if (value) {  // nextBidi
00996         // aPrevInFlow and this frame will point to the same next bidi frame.
00997         propTable->SetProperty(this, nsLayoutAtoms::nextBidi,
00998                                value, nsnull, nsnull);
00999 
01000         ( (nsIFrame*) value)->GetOffsets(start, end);
01001         mContentLength = PR_MAX(1, start - mContentOffset);
01002       } // value
01003       mState |= NS_FRAME_IS_BIDI;
01004     } // prev frame is bidi
01005 #endif // IBMBIDI
01006   }
01007 
01008   return rv;
01009 }
01010 
01011 NS_IMETHODIMP
01012 nsContinuingTextFrame::Destroy(nsPresContext* aPresContext)
01013 {
01014   if (mPrevInFlow || mNextInFlow) {
01015     nsSplittableFrame::RemoveFromFlow(this);
01016   }
01017   // Let the base class destroy the frame
01018   return nsFrame::Destroy(aPresContext);
01019 }
01020 
01021 nsIFrame*
01022 nsContinuingTextFrame::GetFirstInFlow() const
01023 {
01024   // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
01025   nsIFrame *firstInFlow,
01026            *previous = NS_CONST_CAST(nsIFrame*,
01027                                      NS_STATIC_CAST(const nsIFrame*, this));
01028   do {
01029     firstInFlow = previous;
01030     previous = firstInFlow->GetPrevInFlow();
01031   } while (previous);
01032   return firstInFlow;
01033 }
01034 
01035 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB)
01036 {
01037     if (colorA == colorB)
01038     {
01039       nscolor res;
01040       res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
01041                    NS_GET_G(colorA) ^ 0xff,
01042                    NS_GET_B(colorA) ^ 0xff);
01043       return res;
01044     }
01045     return colorA;
01046 }
01047 
01048 
01049 //DRAW SELECTION ITERATOR USED FOR TEXTFRAMES ONLY
01050 //helper class for drawing multiply selected text
01051 class DrawSelectionIterator
01052 {
01053   enum {SELECTION_TYPES_WE_CARE_ABOUT=nsISelectionController::SELECTION_NONE+nsISelectionController::SELECTION_NORMAL};
01054 public:
01055   DrawSelectionIterator(nsIContent *aContent, const SelectionDetails *aSelDetails, PRUnichar *aText,
01056                         PRUint32 aTextLength, nsTextFrame::TextPaintStyle &aTextStyle,
01057                         PRInt16 aSelectionStatus, nsPresContext *aPresContext,
01058                         nsStyleContext *aStyleContext);
01059   ~DrawSelectionIterator();
01060   PRBool      First();
01061   PRBool      Next();
01062   PRBool      IsDone();
01063   PRBool      IsLast();
01064 
01065   PRUnichar * CurrentTextUnicharPtr();
01066   char *      CurrentTextCStrPtr();
01067   PRUint32    CurrentLength();
01068   nsTextFrame::TextPaintStyle & CurrentStyle();
01069   PRBool      IsBeforeOrAfter();
01070 
01084   PRBool GetSelectionColors(nscolor *aForeColor, nscolor *aBackColor, PRBool *aBackIsTransparent);
01085 private:
01086   union {
01087     PRUnichar *mUniStr;
01088     char *mCStr;
01089   };
01090   PRUint32  mLength;
01091   PRUint32  mCurrentIdx;
01092   PRUint32  mCurrentLength;
01093   nsTextFrame::TextPaintStyle &mOldStyle;//base new styles on this one???
01094   const SelectionDetails *mDetails;
01095   PRBool    mDone;
01096   PRUint8 * mTypes;
01097   PRBool    mInit;
01098   PRInt16    mSelectionStatus;//see nsIDocument.h SetDisplaySelection()
01099   nscolor   mFrameBackgroundColor;
01100   PRInt32   mSufficientContrast;
01101   nscolor   mDisabledColor;
01102   nscolor   mAttentionColor;
01103 
01104   PRBool    mSelectionPseudoStyle;
01105   nscolor   mSelectionPseudoFGcolor;
01106   nscolor   mSelectionPseudoBGcolor;
01107   PRBool    mSelectionPseudoBGIsTransparent;
01108   //private methods
01109   void FillCurrentData();
01110 };
01111 
01112 DrawSelectionIterator::DrawSelectionIterator(nsIContent *aContent,
01113                                              const SelectionDetails *aSelDetails, 
01114                                              PRUnichar *aText, 
01115                                              PRUint32 aTextLength, 
01116                                              nsTextFrame::TextPaintStyle &aTextStyle, 
01117                                              PRInt16 aSelectionStatus, 
01118                                              nsPresContext *aPresContext,
01119                                              nsStyleContext *aStyleContext)
01120                                              :mOldStyle(aTextStyle)
01121 {
01122     mDetails = aSelDetails;
01123     mCurrentIdx = 0;
01124     mUniStr = aText;
01125     mLength = aTextLength;
01126     mTypes = nsnull;
01127     mInit = PR_FALSE;
01128     mSelectionStatus = aSelectionStatus;
01129     mSelectionPseudoStyle = PR_FALSE;
01130     mSelectionPseudoBGIsTransparent = PR_FALSE;
01131 
01132     const nsStyleBackground* bg =
01133       nsCSSRendering::FindNonTransparentBackground(aStyleContext);
01134     NS_ASSERTION(bg, "Cannot find NonTransparentBackground.");
01135     mFrameBackgroundColor = bg->mBackgroundColor;
01136 
01137     if (aContent) {
01138       nsRefPtr<nsStyleContext> sc;
01139       sc = aPresContext->StyleSet()->
01140         ProbePseudoStyleFor(aContent->GetParent(),
01141                             nsCSSPseudoElements::mozSelection, aStyleContext);
01142       if (sc) {
01143         mSelectionPseudoStyle = PR_TRUE;
01144         bg = sc->GetStyleBackground();
01145         mSelectionPseudoBGIsTransparent = PRBool(bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT);
01146         if (!mSelectionPseudoBGIsTransparent )
01147           mSelectionPseudoBGcolor = bg->mBackgroundColor;
01148         mSelectionPseudoFGcolor = sc->GetStyleColor()->mColor;
01149       }
01150     }
01151 
01152     // Get background colors for disabled selection at attention-getting selection (used with type ahead find)
01153     nsILookAndFeel *look = aPresContext->LookAndFeel();
01154     nscolor defaultWindowBackgroundColor;
01155     look->GetColor(nsILookAndFeel::eColor_WindowBackground,
01156                    defaultWindowBackgroundColor);
01157     look->GetColor(nsILookAndFeel::eColor_TextSelectBackgroundAttention,
01158                    mAttentionColor);
01159     look->GetColor(nsILookAndFeel::eColor_TextSelectBackgroundDisabled,
01160                    mDisabledColor);
01161     mDisabledColor  = EnsureDifferentColors(mDisabledColor,
01162                                             mOldStyle.mSelectionBGColor);
01163     mAttentionColor = EnsureDifferentColors(mAttentionColor,
01164                                             mOldStyle.mSelectionBGColor);
01165 
01166     mSufficientContrast =
01167       PR_MIN(PR_MIN(NS_SUFFICIENT_LUMINOSITY_DIFFERENCE,
01168                     NS_LUMINOSITY_DIFFERENCE(mOldStyle.mSelectionTextColor,
01169                                              mOldStyle.mSelectionBGColor)),
01170                     NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor,
01171                                              mOldStyle.mSelectionBGColor));
01172 
01173     if (!aSelDetails)
01174     {
01175       mDone = PR_TRUE;
01176       return;
01177     }
01178     mDone = (PRBool)(mCurrentIdx>=mLength);
01179     if (mDone)
01180       return;
01181 
01182     //special case for 1 selection. later
01183     const SelectionDetails *details = aSelDetails;
01184     if (details->mNext)
01185     {
01186       mTypes = new PRUint8[mLength];
01187       if (!mTypes)
01188         return;
01189       memset(mTypes,0,mLength);//initialize to 0
01190       while (details)
01191       {
01192         if ((details->mType & SELECTION_TYPES_WE_CARE_ABOUT ) && 
01193           (details->mStart != details->mEnd))
01194         {
01195           mInit = PR_TRUE;//WE FOUND SOMETHING WE CARE ABOUT
01196           for (int i = details->mStart; i < details->mEnd; i++)
01197           {
01198               if ((PRUint32)i>=mLength)
01199               {
01200                 NS_ASSERTION(0,"Selection Details out of range?");
01201                 return;//eh
01202               }
01203               mTypes[i]|=details->mType;//add this bit
01204           }
01205         }
01206         details= details->mNext;
01207       }
01208       if (!mInit && mTypes) //we have details but none that we care about.
01209       {
01210         delete [] mTypes;
01211         mTypes = nsnull;
01212         mDone = PR_TRUE;//we are finished
01213       }
01214     }
01215     else if (details->mStart == details->mEnd)//no collapsed selections here!
01216     {
01217       mDone = PR_TRUE;
01218       return;
01219     }
01220     else if (!(details->mType & SELECTION_TYPES_WE_CARE_ABOUT ))//if all we have is selection we DONT care about, do nothing
01221     {
01222         mDone = PR_TRUE;
01223         return;
01224     }
01225     mInit = PR_TRUE;
01226 }
01227 
01228 DrawSelectionIterator::~DrawSelectionIterator()
01229 {
01230   if (mTypes)
01231     delete [] mTypes;
01232 }
01233 
01234 void
01235 DrawSelectionIterator::FillCurrentData()
01236 {
01237   if (mDone)
01238     return;
01239   mCurrentIdx += mCurrentLength; // advance to this chunk
01240   mCurrentLength = 0;
01241   if (mCurrentIdx >= mLength)
01242   {
01243     mDone = PR_TRUE;
01244     return;
01245   }
01246   if (!mTypes)
01247   {
01248     if (mCurrentIdx < (PRUint32)mDetails->mStart)
01249     {
01250       mCurrentLength = mDetails->mStart;
01251     }
01252     else if (mCurrentIdx == (PRUint32)mDetails->mStart)
01253     {//start
01254         mCurrentLength = mDetails->mEnd-mCurrentIdx;
01255     }
01256     else if (mCurrentIdx > (PRUint32)mDetails->mStart)//last unselected part
01257     {
01258       mCurrentLength = mLength - mDetails->mEnd;
01259     }
01260   }
01261   else
01262   {
01263     uint8 typevalue = mTypes[mCurrentIdx];
01264     while (mCurrentIdx+mCurrentLength < mLength && typevalue == mTypes[mCurrentIdx+mCurrentLength])
01265     {
01266       mCurrentLength++;
01267     }
01268   }
01269   // never overrun past mLength
01270   if (mCurrentIdx+mCurrentLength > mLength)
01271   {
01272     mCurrentLength = mLength - mCurrentIdx;
01273   }
01274 }
01275 
01276 PRBool
01277 DrawSelectionIterator::First()
01278 {
01279   if (!mInit)
01280     return PR_FALSE;
01281   mCurrentIdx = 0;
01282   mCurrentLength = 0;
01283   if (!mTypes && mDetails->mStart == mDetails->mEnd)//no collapsed selections here!
01284     mDone = PR_TRUE;
01285   mDone = (mCurrentIdx+mCurrentLength) >= mLength;
01286   FillCurrentData();
01287   return PR_TRUE;
01288 }
01289 
01290 
01291 
01292 PRBool
01293 DrawSelectionIterator::Next()
01294 {
01295   if (mDone || !mInit)
01296     return PR_FALSE;
01297   FillCurrentData();//advances to next chunk
01298   return PR_TRUE;
01299 }
01300 
01301 PRBool
01302 DrawSelectionIterator::IsLast()
01303 {
01304  return mDone || !mInit || mCurrentIdx + mCurrentLength >= mLength;
01305 }
01306 
01307 PRBool
01308 DrawSelectionIterator::IsDone()
01309 {
01310     return mDone || !mInit;
01311 }
01312 
01313 
01314 PRUnichar *
01315 DrawSelectionIterator::CurrentTextUnicharPtr()
01316 {
01317   return mUniStr+mCurrentIdx;
01318 }
01319 
01320 char *
01321 DrawSelectionIterator::CurrentTextCStrPtr()
01322 {
01323   return mCStr+mCurrentIdx;
01324 }
01325 
01326 PRUint32
01327 DrawSelectionIterator::CurrentLength()
01328 {
01329   return mCurrentLength;
01330 }
01331 
01332 nsTextFrame::TextPaintStyle & 
01333 DrawSelectionIterator::CurrentStyle()
01334 {
01335   return mOldStyle;
01336 }
01337 
01338 PRBool
01339 DrawSelectionIterator::GetSelectionColors(nscolor *aForeColor,
01340                                           nscolor *aBackColor,
01341                                           PRBool  *aBackIsTransparent)
01342 {
01343   *aBackIsTransparent = PR_FALSE;
01344   PRBool isSelection =
01345     (mTypes && (mTypes[mCurrentIdx] & nsISelectionController::SELECTION_NORMAL)) ||
01346     (!mTypes && mCurrentIdx == (PRUint32)mDetails->mStart);
01347   if (!isSelection) {
01348     *aForeColor = mOldStyle.mColor->mColor;
01349     return PR_FALSE;
01350   }
01351 
01352   if (mSelectionPseudoStyle &&
01353       mSelectionStatus == nsISelectionController::SELECTION_ON) {
01354     *aForeColor = mSelectionPseudoFGcolor;
01355     *aBackColor = mSelectionPseudoBGcolor;
01356     *aBackIsTransparent = mSelectionPseudoBGIsTransparent;
01357     return PR_TRUE;
01358   }
01359 
01360   PRBool dontChangeTextColor =
01361            mOldStyle.mSelectionTextColor == NS_DONT_CHANGE_COLOR;
01362 
01363   if (dontChangeTextColor)
01364     *aForeColor = mOldStyle.mColor->mColor;
01365   else
01366     *aForeColor = mOldStyle.mSelectionTextColor;
01367 
01368   if (mSelectionStatus == nsISelectionController::SELECTION_ATTENTION)
01369     *aBackColor = mAttentionColor;
01370   else if (mSelectionStatus != nsISelectionController::SELECTION_ON)
01371     *aBackColor = mDisabledColor;
01372   else
01373     *aBackColor = mOldStyle.mSelectionBGColor;
01374 
01375   // We don't support selection color exchanging when selection text color is
01376   // NS_DONT_CHANGE_COLOR. Because the text color should not be background color.
01377   if (dontChangeTextColor) {
01378     *aForeColor = EnsureDifferentColors(*aForeColor, *aBackColor);
01379     return PR_TRUE;
01380   }
01381 
01382   // If the combination of selection background color and frame background color
01383   // is sufficient contrast, don't exchange the selection colors.
01384   PRInt32 backLuminosityDifference =
01385             NS_LUMINOSITY_DIFFERENCE(*aBackColor, mFrameBackgroundColor);
01386   if (backLuminosityDifference >= mSufficientContrast)
01387     return PR_TRUE;
01388 
01389   // Otherwise, we should use the higher-contrast color for the selection
01390   // background color.
01391   PRInt32 foreLuminosityDifference =
01392             NS_LUMINOSITY_DIFFERENCE(*aForeColor, mFrameBackgroundColor);
01393   if (backLuminosityDifference < foreLuminosityDifference) {
01394     nscolor tmpColor = *aForeColor;
01395     *aForeColor = *aBackColor;
01396     *aBackColor = tmpColor;
01397   }
01398   return PR_TRUE;
01399 }
01400 
01401 PRBool
01402 DrawSelectionIterator::IsBeforeOrAfter()
01403 {
01404   return mCurrentIdx != (PRUint32)mDetails->mStart;
01405 }
01406 
01407 //END DRAWSELECTIONITERATOR!!
01408 
01409 
01410 
01411 
01412 // Flag information used by rendering code. This information is
01413 // computed by the ResizeReflow code. The flags are stored in the
01414 // mState variable in the frame class private section.
01415 
01416 // Flag indicating that whitespace was skipped
01417 #define TEXT_SKIP_LEADING_WS 0x01000000
01418 #define TEXT_HAS_MULTIBYTE   0x02000000
01419 #define TEXT_IN_WORD         0x04000000
01420 // This bit is set on the first frame in a continuation indicating
01421 // that it was chopped short because of :first-letter style.
01422 #define TEXT_FIRST_LETTER    0x08000000
01423 #define TEXT_WAS_TRANSFORMED 0x10000000
01424 
01425 // Bits in mState used for reflow flags
01426 #define TEXT_REFLOW_FLAGS    0x1F000000
01427 
01428 #define TEXT_TRIMMED_WS      0x20000000
01429 
01430 #define TEXT_OPTIMIZE_RESIZE 0x40000000
01431 
01432 #define TEXT_BLINK_ON        0x80000000
01433 
01434 #define TEXT_IS_ONLY_WHITESPACE    0x00100000
01435 
01436 #define TEXT_ISNOT_ONLY_WHITESPACE 0x00200000
01437 
01438 #define TEXT_WHITESPACE_FLAGS      0x00300000
01439 
01440 #define TEXT_IS_END_OF_LINE        0x00400000
01441 
01442 //----------------------------------------------------------------------
01443 
01444 #if defined(DEBUG_rbs) || defined(DEBUG_bzbarsky)
01445 static void
01446 VerifyNotDirty(nsFrameState state)
01447 {
01448   PRBool isZero = state & NS_FRAME_FIRST_REFLOW;
01449   PRBool isDirty = state & NS_FRAME_IS_DIRTY;
01450   if (!isZero && isDirty)
01451     NS_WARNING("internal offsets may be out-of-sync");
01452 }
01453 #define DEBUG_VERIFY_NOT_DIRTY(state) \
01454 VerifyNotDirty(state)
01455 #else
01456 #define DEBUG_VERIFY_NOT_DIRTY(state)
01457 #endif
01458 
01459 nsresult
01460 NS_NewTextFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
01461 {
01462   NS_PRECONDITION(aNewFrame, "null OUT ptr");
01463   if (nsnull == aNewFrame) {
01464     return NS_ERROR_NULL_POINTER;
01465   }
01466   nsTextFrame* it = new (aPresShell) nsTextFrame;
01467   if (nsnull == it) {
01468     return NS_ERROR_OUT_OF_MEMORY;
01469   }
01470   *aNewFrame = it;
01471   return NS_OK;
01472 }
01473 
01474 nsresult
01475 NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
01476 {
01477   NS_PRECONDITION(aNewFrame, "null OUT ptr");
01478   if (nsnull == aNewFrame) {
01479     return NS_ERROR_NULL_POINTER;
01480   }
01481   nsContinuingTextFrame* it = new (aPresShell) nsContinuingTextFrame;
01482   if (nsnull == it) {
01483     return NS_ERROR_OUT_OF_MEMORY;
01484   }
01485   *aNewFrame = it;
01486   return NS_OK;
01487 }
01488 
01489 nsTextFrame::nsTextFrame()
01490 {
01491 }
01492 
01493 nsTextFrame::~nsTextFrame()
01494 {
01495   if (0 != (mState & TEXT_BLINK_ON))
01496   {
01497     nsBlinkTimer::RemoveBlinkFrame(this);
01498   }
01499 }
01500 
01501 nsIDocument*
01502 nsTextFrame::GetDocument(nsPresContext* aPresContext)
01503 {
01504   nsIDocument *result = nsnull;
01505   if (mContent) {
01506     result = mContent->GetDocument();
01507   }
01508   if (!result && aPresContext) {
01509     result = aPresContext->PresShell()->GetDocument();
01510   }
01511   return result;
01512 }
01513 
01514 NS_IMETHODIMP
01515 nsTextFrame::GetCursor(const nsPoint& aPoint,
01516                        nsIFrame::Cursor& aCursor)
01517 {
01518   FillCursorInformationFromStyle(GetStyleUserInterface(), aCursor);  
01519   if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
01520     aCursor.mCursor = NS_STYLE_CURSOR_TEXT;
01521 
01522     // If tabindex >= 0, use default cursor to indicate it's not selectable
01523     nsIFrame *ancestorFrame = this;
01524     while ((ancestorFrame = ancestorFrame->GetParent()) != nsnull) {
01525       nsIContent *ancestorContent = ancestorFrame->GetContent();
01526       if (ancestorContent && ancestorContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex)) {
01527         nsAutoString tabIndexStr;
01528         ancestorContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex, tabIndexStr);
01529         if (!tabIndexStr.IsEmpty()) {
01530           PRInt32 rv, tabIndexVal = tabIndexStr.ToInteger(&rv);
01531           if (NS_SUCCEEDED(rv) && tabIndexVal >= 0) {
01532             aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
01533             break;
01534           }
01535         }
01536       }
01537     }
01538   }
01539 
01540   return NS_OK;
01541 }
01542 
01543 nsIFrame*
01544 nsTextFrame::GetLastInFlow() const
01545 {
01546   nsTextFrame* lastInFlow = (nsTextFrame*)this;
01547   while (lastInFlow->mNextInFlow)  {
01548     lastInFlow = (nsTextFrame*)lastInFlow->mNextInFlow;
01549   }
01550   NS_POSTCONDITION(lastInFlow, "illegal state in flow chain.");
01551   return lastInFlow;
01552 }
01553 
01554 NS_IMETHODIMP
01555 nsTextFrame::CharacterDataChanged(nsPresContext* aPresContext,
01556                                   nsIContent*     aChild,
01557                                   PRBool          aAppend)
01558 {
01559   nsIFrame* targetTextFrame = this;
01560 
01561   PRBool markAllDirty = PR_TRUE;
01562   if (aAppend) {
01563     markAllDirty = PR_FALSE;
01564     nsTextFrame* frame = (nsTextFrame*)GetLastInFlow();
01565     frame->mState &= ~TEXT_WHITESPACE_FLAGS;
01566     frame->mState |= NS_FRAME_IS_DIRTY;
01567     targetTextFrame = frame;
01568   }
01569 
01570   if (markAllDirty) {
01571     // Mark this frame and all the next-in-flow frames as dirty
01572     nsTextFrame*  textFrame = this;
01573     nsPropertyTable *propTable = aPresContext->PropertyTable();
01574     while (textFrame) {
01575       textFrame->mState &= ~TEXT_WHITESPACE_FLAGS;
01576       textFrame->mState |= NS_FRAME_IS_DIRTY;
01577 #ifdef IBMBIDI
01578       void* nextBidiFrame;
01579       if ((textFrame->mState & NS_FRAME_IS_BIDI) &&
01580           (nextBidiFrame = propTable->GetProperty(textFrame, nsLayoutAtoms::nextBidi)))
01581         textFrame = (nsTextFrame*)nextBidiFrame;
01582       else
01583 #endif
01584         textFrame = (nsTextFrame*)textFrame->mNextInFlow;
01585     }
01586   }
01587 
01588   // Ask the parent frame to reflow me.  
01589   nsIPresShell *shell = aPresContext->GetPresShell();
01590   if (shell && mParent) {
01591     mParent->ReflowDirtyChild(shell, targetTextFrame);
01592   }
01593   
01594 
01595   return NS_OK;
01596 }
01597 
01598 NS_IMETHODIMP
01599 nsTextFrame::Paint(nsPresContext*      aPresContext,
01600                    nsIRenderingContext& aRenderingContext,
01601                    const nsRect&        aDirtyRect,
01602                    nsFramePaintLayer    aWhichLayer,
01603                    PRUint32             aFlags)
01604 {
01605   if (NS_FRAME_PAINT_LAYER_FOREGROUND != aWhichLayer) {
01606     return NS_OK;
01607   }
01608   if ((0 != (mState & TEXT_BLINK_ON)) && nsBlinkTimer::GetBlinkIsOff()) {
01609     return NS_OK;
01610   }
01611   nsStyleContext* sc = mStyleContext;
01612   PRBool isVisible;
01613   if (NS_SUCCEEDED(IsVisibleForPainting(aPresContext, aRenderingContext, PR_TRUE, &isVisible)) && isVisible) {
01614     TextPaintStyle ts(aPresContext, aRenderingContext, mStyleContext);
01615     if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing)
01616       || ts.mJustifying) {
01617       PaintTextSlowly(aPresContext, aRenderingContext, sc, ts, 0, 0);
01618     }
01619     else {
01620       // Get the text fragment
01621       nsCOMPtr<nsITextContent> tc = do_QueryInterface(mContent);
01622       const nsTextFragment* frag = nsnull;
01623       if (tc) {
01624         frag = tc->Text();
01625         if (!frag) {
01626           return NS_ERROR_FAILURE;
01627         }
01628       }
01629 
01630       // Choose rendering pathway based on rendering context performance
01631       // hint, whether it needs to be transformed, and whether it's
01632       // multi-byte
01633       PRBool   hasMultiByteChars = (0 != (mState & TEXT_HAS_MULTIBYTE));
01634       PRUint32 hints = 0;
01635       aRenderingContext.GetHints(hints);
01636 
01637 #ifdef IBMBIDI
01638       PRBool bidiEnabled = aPresContext->BidiEnabled();
01639 #else
01640       const PRBool bidiEnabled = PR_FALSE;
01641 #endif // IBMBIDI
01642       // * BiDi text or text with multi-byte characters must always be
01643       //   rendered as Unicode.
01644       // * Non-transformed, 1-byte text should always be rendered as
01645       //   ASCII.
01646       // * Other transformed or 2-byte text should be rendered according
01647       //   to the preference of the hint from the rendering context.
01648       if (bidiEnabled || hasMultiByteChars ||
01649           ((0 == (hints & NS_RENDERING_HINT_FAST_8BIT_TEXT)) &&
01650            (frag->Is2b() || (0 != (mState & TEXT_WAS_TRANSFORMED))))) {
01651         PaintUnicodeText(aPresContext, aRenderingContext, sc, ts, 0, 0);
01652       }
01653       else {
01654         PaintAsciiText(aPresContext, aRenderingContext, sc, ts, 0, 0);
01655       }
01656 
01657     }
01658   }
01659   DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame", &aRenderingContext);
01660   return NS_OK;
01661 }
01662 
01663 PRBool
01664 nsTextFrame::IsChineseJapaneseLangGroup()
01665 {
01666   const nsStyleVisibility* visibility = mStyleContext->GetStyleVisibility();
01667   if (visibility->mLangGroup == nsLayoutAtoms::Japanese
01668       || visibility->mLangGroup == nsLayoutAtoms::Chinese
01669       || visibility->mLangGroup == nsLayoutAtoms::Taiwanese
01670       || visibility->mLangGroup == nsLayoutAtoms::HongKongChinese)
01671     return PR_TRUE;
01672 
01673   return PR_FALSE;
01674 }
01675 
01676 /*
01677  * Currently only Unicode characters below 0x10000 have their spacing modified
01678  * by justification. If characters above 0x10000 turn out to need
01679  * justification spacing, that will require extra work. Currently,
01680  * this function must not include 0xd800 to 0xdbff because these characters
01681  * are surrogates.
01682  */
01683 PRBool
01684 nsTextFrame::IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ)
01685 {
01686   if (0x20u == aChar || 0xa0u == aChar)
01687     return PR_TRUE;
01688   if (aChar < 0x2150u)
01689     return PR_FALSE;
01690   if (aLangIsCJ && (
01691        (0x2150u <= aChar && aChar <= 0x22ffu) || // Number Forms, Arrows, Mathematical Operators
01692        (0x2460u <= aChar && aChar <= 0x24ffu) || // Enclosed Alphanumerics
01693        (0x2580u <= aChar && aChar <= 0x27bfu) || // Block Elements, Geometric Shapes, Miscellaneous Symbols, Dingbats
01694        (0x27f0u <= aChar && aChar <= 0x2bffu) || // Supplemental Arrows-A, Braille Patterns, Supplemental Arrows-B,
01695                                                  // Miscellaneous Mathematical Symbols-B, Supplemental Mathematical Operators,
01696                                                  // Miscellaneous Symbols and Arrows
01697        (0x2e80u <= aChar && aChar <= 0x312fu) || // CJK Radicals Supplement, CJK Radicals Supplement,
01698                                                  // Ideographic Description Characters, CJK Symbols and Punctuation,
01699                                                  // Hiragana, Katakana, Bopomofo
01700        (0x3190u <= aChar && aChar <= 0xabffu) || // Kanbun, Bopomofo Extended, Katakana Phonetic Extensions,
01701                                                  // Enclosed CJK Letters and Months, CJK Compatibility,
01702                                                  // CJK Unified Ideographs Extension A, Yijing Hexagram Symbols,
01703                                                  // CJK Unified Ideographs, Yi Syllables, Yi Radicals
01704        (0xf900u <= aChar && aChar <= 0xfaffu) || // CJK Compatibility Ideographs
01705        (0xff5eu <= aChar && aChar <= 0xff9fu)    // Halfwidth and Fullwidth Forms(a part)
01706      ))
01707     return PR_TRUE;
01708   return PR_FALSE;
01709 }
01710 
01711 nsresult
01712 nsTextFrame::FillClusterBuffer(nsPresContext *aPresContext, const PRUnichar *aText,
01713                                PRUint32 aLength, nsAutoPRUint8Buffer& aClusterBuffer)
01714 {
01715   nsresult rv = aClusterBuffer.GrowTo(aLength);
01716   NS_ENSURE_SUCCESS(rv, rv);
01717 
01718   // Fill in the cluster hint information, if it's available.
01719   nsCOMPtr<nsIRenderingContext> acx;
01720   PRUint32 clusterHint = 0;
01721 
01722   nsIPresShell *shell = aPresContext->GetPresShell();
01723   if (shell) {
01724     rv = shell->CreateRenderingContext(this, getter_AddRefs(acx));
01725     NS_ENSURE_SUCCESS(rv, rv);
01726 
01727     // Find the font metrics for this text
01728     SetFontFromStyle(acx, mStyleContext);
01729 
01730     acx->GetHints(clusterHint);
01731     clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
01732   }
01733 
01734   if (clusterHint) {
01735     rv = acx->GetClusterInfo(aText, aLength, aClusterBuffer.mBuffer);
01736   }
01737   else {
01738     memset(aClusterBuffer.mBuffer, 1, sizeof(PRInt8) * aLength);
01739   }
01740 
01741   return rv;
01742 }
01743 
01744 inline PRBool IsEndOfLine(nsFrameState aState)
01745 {
01746   return (aState & TEXT_IS_END_OF_LINE) ? PR_TRUE : PR_FALSE;
01747 }
01748 
01754 void
01755 nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX,
01756                                 nsAutoIndexBuffer* aIndexBuffer,
01757                                 nsAutoTextBuffer* aTextBuffer,
01758                                 PRInt32* aTextLen,
01759                                 PRBool aForceArabicShaping,
01760                                 PRIntn* aJustifiableCharCount)
01761 {
01762   // Setup transform to operate starting in the content at our content
01763   // offset
01764   aTX.Init(this, mContent, mContentOffset, aForceArabicShaping);
01765 
01766   PRInt32 strInx = mContentOffset;
01767   PRInt32* indexp = aIndexBuffer ? aIndexBuffer->mBuffer : nsnull;
01768 
01769   // Skip over the leading whitespace
01770   PRInt32 n = mContentLength;
01771   if (0 != (mState & TEXT_SKIP_LEADING_WS)) {
01772     PRBool isWhitespace, wasTransformed;
01773     PRInt32 wordLen, contentLen;
01774     // Set maximum word length. This is an ABUSE of the variable
01775     // because on entry, this is DOM content length, but on exit,
01776     // GetNextWord returns a transformed-string length in here!
01777     wordLen = mContentOffset + mContentLength;
01778     aTX.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed);
01779     // we trip this assertion in bug 31053, but I think it's unnecessary
01780     //NS_ASSERTION(isWhitespace, "mState and content are out of sync");
01781 
01782     if (isWhitespace) {
01783       if (nsnull != indexp) {
01784         // Point mapping indicies at the same content index since
01785         // all of the compressed whitespace maps down to the same
01786         // renderable character.
01787         PRInt32 i = contentLen;
01788         while (--i >= 0) {
01789           *indexp++ = strInx;
01790         }
01791       }
01792       n -= contentLen;
01793       if(n<0)
01794         NS_WARNING("mContentLength is < FragmentLength");
01795     }
01796   }
01797 
01798   // Rescan the content and transform it. Stop when we have consumed
01799   // mContentLength characters.
01800   PRUint8 textTransform = GetStyleText()->mTextTransform;
01801   PRBool inWord = (TEXT_IN_WORD & mState) ? PR_TRUE : PR_FALSE;
01802   PRInt32 column = mColumn;
01803   PRInt32 textLength = 0;
01804   PRInt32 dstOffset = 0;
01805 
01806   nsAutoTextBuffer tmpTextBuffer;
01807   nsAutoTextBuffer* textBuffer = aTextBuffer;
01808   if (!textBuffer && aJustifiableCharCount)
01809     textBuffer = &tmpTextBuffer;
01810 
01811   while (n > 0) {
01812     PRUnichar* bp;
01813     PRBool isWhitespace, wasTransformed;
01814     PRInt32 wordLen, contentLen;
01815 
01816     // Set maximum word length. This is an ABUSE of the variable
01817     // because on entry, this is DOM content length, but on exit,
01818     // GetNextWord returns a transformed-string length in here!
01819     wordLen = mContentOffset + mContentLength;
01820     // Get the next word
01821     bp = aTX.GetNextWord(inWord, &wordLen, &contentLen, &isWhitespace, &wasTransformed);
01822     if (nsnull == bp) {
01823       if (indexp) {
01824         while (--n >= 0) {
01825           *indexp++ = strInx;
01826         }
01827       }
01828       break;
01829     }
01830     inWord = PR_FALSE;
01831     if (isWhitespace) {
01832       if ('\t' == bp[0]) {
01833         PRInt32 spaces = 8 - (7 & column);
01834         PRUnichar* tp = bp;
01835         wordLen = spaces;
01836         while (--spaces >= 0) {
01837           *tp++ = ' ';
01838         }
01839         // XXX This is a one to many mapping that I think isn't handled well
01840         if (nsnull != indexp) {
01841           *indexp++ = strInx;
01842           strInx += wordLen;
01843         }
01844       }
01845       else if ('\n' == bp[0]) {
01846         if (nsnull != indexp) {
01847           *indexp++ = strInx;
01848         }
01849         break;
01850       }
01851       else if (nsnull != indexp) {
01852         if (1 == wordLen) {
01853           // Point mapping indicies at the same content index since
01854           // all of the compressed whitespace maps down to the same
01855           // renderable character.
01856           PRInt32 i = contentLen;
01857           while (--i >= 0) {
01858             *indexp++ = strInx;
01859           }
01860           strInx++;
01861         } else {
01862           // Point mapping indicies at each content index in the word
01863           PRInt32 i = contentLen;
01864           while (--i >= 0) {
01865             *indexp++ = strInx++;
01866           }
01867         }
01868       }
01869     }
01870     else {
01871       PRInt32 i = contentLen;
01872       if (nsnull != indexp) {
01873         // Point mapping indices at each content index in the word
01874         if (!wasTransformed) {
01875           while (--i >= 0) {
01876             *indexp++ = strInx++;
01877           }
01878         } else {
01879           PRUnichar* tp = bp;
01880           PRBool caseChanged = 
01881             textTransform == NS_STYLE_TEXT_TRANSFORM_UPPERCASE ||
01882             textTransform == NS_STYLE_TEXT_TRANSFORM_CAPITALIZE;
01883           while (--i >= 0) {
01884             PRUnichar ch = aTX.GetContentCharAt(mContentOffset +
01885                              indexp - aIndexBuffer->mBuffer);
01886             if (IS_DISCARDED(ch) || ch == '\n') {
01887               *indexp++ = strInx;
01888               continue;
01889             }
01890             *indexp++ = strInx++;
01891             // Point any capitalized German &szlig; to 'SS'
01892             if (caseChanged && ch == kSZLIG && *tp == PRUnichar('S')) {
01893               ++strInx;
01894               ++tp;
01895             }
01896             ++tp;
01897           }
01898         }
01899       }
01900     }
01901 
01902     // Grow the buffer before we run out of room.
01903     if (textBuffer != nsnull && dstOffset + wordLen > textBuffer->mBufferLen) {
01904       nsresult rv = textBuffer->GrowBy(wordLen);
01905       if (NS_FAILED(rv)) {
01906         break;
01907       }
01908     }
01909 
01910     column += wordLen;
01911     textLength += wordLen;
01912     n -= contentLen;
01913     if (textBuffer != nsnull) {
01914       memcpy(textBuffer->mBuffer + dstOffset, bp,
01915              sizeof(PRUnichar)*wordLen);
01916     }
01917     dstOffset += wordLen;
01918   }
01919 
01920 #ifdef DEBUG
01921   if (aIndexBuffer) {
01922     NS_ASSERTION(indexp <= aIndexBuffer->mBuffer + aIndexBuffer->mBufferLen,
01923                  "yikes - we just overwrote memory");
01924   }
01925   if (textBuffer) {
01926     NS_ASSERTION(dstOffset <= textBuffer->mBufferLen,
01927                  "yikes - we just overwrote memory");
01928   }
01929 
01930 #endif
01931 
01932   // Remove trailing whitespace if it was trimmed after reflow
01933   // TEXT_TRIMMED_WS can be set in measureText during reflow, and 
01934   // nonexitent text buffer may occur in this situation.
01935   if (TEXT_TRIMMED_WS & mState && textBuffer) {
01936     if (--dstOffset >= 0) {
01937       PRUnichar ch = textBuffer->mBuffer[dstOffset];
01938       if (XP_IS_SPACE(ch))
01939         textLength--;
01940     }
01941   }
01942 
01943   if (aIndexBuffer) {
01944     PRInt32* ip = aIndexBuffer->mBuffer;
01945     ip[mContentLength] = ip[mContentLength-1];
01946     if ((ip[mContentLength] - mContentOffset) < textLength) {
01947       // Must set up last one for selection beyond edge if in boundary
01948       ip[mContentLength] = textLength + mContentOffset;
01949     }
01950   }
01951 
01952   *aTextLen = textLength;
01953 
01954   if (aJustifiableCharCount && textBuffer) {
01955     PRBool isCJ = IsChineseJapaneseLangGroup();
01956     PRIntn numJustifiableCharacter = 0;
01957     PRInt32 justifiableRange = textLength;
01958     if (IsEndOfLine(mState))
01959       justifiableRange--;
01960     for (PRInt32 i = 0; i < justifiableRange; i++) {
01961       if (IsJustifiableCharacter(textBuffer->mBuffer[i], isCJ))
01962         numJustifiableCharacter++;
01963     }
01964     *aJustifiableCharCount = numJustifiableCharacter;
01965   }
01966 }
01967 
01968 
01969 //#define SHOW_SELECTION_CURSOR   // should be turned off when the caret code is activated
01970 
01971 #ifdef SHOW_SELECTION_CURSOR
01972 
01973 // XXX This clearly needs to be done by the container, *somehow*
01974 #define CURSOR_COLOR NS_RGB(0,0,255)
01975 static void
01976 RenderSelectionCursor(nsIRenderingContext& aRenderingContext,
01977                       nscoord dx, nscoord dy, nscoord aHeight,
01978                       nscolor aCursorColor)
01979 {
01980   nsPoint pnts[4];
01981   nscoord ox = aHeight / 4;
01982   nscoord oy = ox;
01983   nscoord x0 = dx;
01984   nscoord y0 = dy + aHeight;
01985   pnts[0].x = x0 - ox;
01986   pnts[0].y = y0;
01987   pnts[1].x = x0;
01988   pnts[1].y = y0 - oy;
01989   pnts[2].x = x0 + ox;
01990   pnts[2].y = y0;
01991   pnts[3].x = x0 - ox;
01992   pnts[3].y = y0;
01993 
01994   // Draw little blue triangle
01995   aRenderingContext.SetColor(aCursorColor);
01996   aRenderingContext.FillPolygon(pnts, 4);
01997 }
01998 
01999 #endif
02000 
02001 // XXX letter-spacing
02002 // XXX word-spacing
02003 #if defined(XP_MACOSX)
02004 #define NO_INVERT
02005 #elif defined(XP_WIN) || defined(XP_OS2) || defined(XP_UNIX)
02006 #define USE_INVERT_FOR_SELECTION
02007 #endif
02008 
02009 // XXX we should get the following from style sheet or LookAndFeel later
02010 #if defined(NO_INVERT)
02011 #define IME_UNDERLINECOLOR NS_RGB(149,149,149)     //light gray
02012 #define IME_SELECTED_UNDERLINECOLOR NS_RGB(0,0,0)  //black
02013 #else
02014 #define IME_RAW_COLOR NS_RGB(198,33,66)
02015 #define IME_CONVERTED_COLOR NS_RGB(255,198,198)
02016 #endif
02017 
02018 void 
02019 nsTextFrame::PaintTextDecorations(nsIRenderingContext& aRenderingContext,
02020                                   nsStyleContext* aStyleContext,
02021                                   nsPresContext* aPresContext,
02022                                   TextPaintStyle& aTextStyle,
02023                                   nscoord aX, nscoord aY, nscoord aWidth,
02024                                   PRBool aRightToLeftText,
02025                                   PRUnichar *aText, /*=nsnull*/
02026                                   SelectionDetails *aDetails,/*= nsnull*/
02027                                   PRUint32 aIndex,  /*= 0*/
02028                                   PRUint32 aLength, /*= 0*/
02029                                   const nscoord* aSpacing /* = nsnull*/ )
02030 
02031 {
02032   // Quirks mode text  decoration are rendered by children; see bug 1777
02033   // In non-quirks mode, nsHTMLContainer::Paint and nsBlockFrame::Paint
02034   // does the painting of text decorations.
02035   if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
02036     nscolor overColor, underColor, strikeColor;
02037   
02038     PRBool useOverride = PR_FALSE;
02039     nscolor overrideColor;
02040 
02041     PRUint8 decorations = NS_STYLE_TEXT_DECORATION_NONE;
02042     // A mask of all possible decorations.
02043     PRUint8 decorMask = NS_STYLE_TEXT_DECORATION_UNDERLINE | 
02044                         NS_STYLE_TEXT_DECORATION_OVERLINE |
02045                         NS_STYLE_TEXT_DECORATION_LINE_THROUGH;    
02046     nsStyleContext* context = aStyleContext;
02047     PRBool hasDecorations = context->HasTextDecorations();
02048 
02049     while (hasDecorations) {
02050       const nsStyleTextReset* styleText = context->GetStyleTextReset();
02051       if (!useOverride && 
02052           (NS_STYLE_TEXT_DECORATION_OVERRIDE_ALL & 
02053            styleText->mTextDecoration)) {
02054         // This handles the <a href="blah.html"><font color="green">La 
02055         // la la</font></a> case. The link underline should be green.
02056         useOverride = PR_TRUE;
02057         overrideColor = context->GetStyleColor()->mColor;          
02058       }
02059 
02060       PRUint8 useDecorations = decorMask & styleText->mTextDecoration;
02061       if (useDecorations) {// a decoration defined here
02062         nscolor color = context->GetStyleColor()->mColor;
02063     
02064         if (NS_STYLE_TEXT_DECORATION_UNDERLINE & useDecorations) {
02065           underColor = useOverride ? overrideColor : color;
02066           decorMask &= ~NS_STYLE_TEXT_DECORATION_UNDERLINE;
02067           decorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE;
02068         }
02069         if (NS_STYLE_TEXT_DECORATION_OVERLINE & useDecorations) {
02070           overColor = useOverride ? overrideColor : color;
02071           decorMask &= ~NS_STYLE_TEXT_DECORATION_OVERLINE;
02072           decorations |= NS_STYLE_TEXT_DECORATION_OVERLINE;
02073         }
02074         if (NS_STYLE_TEXT_DECORATION_LINE_THROUGH & useDecorations) {
02075           strikeColor = useOverride ? overrideColor : color;
02076           decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
02077           decorations |= NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
02078         }
02079       }
02080       if (0 == decorMask)
02081         break;
02082       context = context->GetParent();
02083       if (!context)
02084         break;
02085       hasDecorations = context->HasTextDecorations();
02086     }
02087 
02088     nscoord offset;
02089     nscoord size;
02090     nscoord baseline = mAscent;
02091     if (decorations & (NS_FONT_DECORATION_OVERLINE |
02092                        NS_FONT_DECORATION_UNDERLINE)) {
02093       aTextStyle.mNormalFont->GetUnderline(offset, size);
02094       if (decorations & NS_FONT_DECORATION_OVERLINE) {
02095         aRenderingContext.SetColor(overColor);
02096         aRenderingContext.FillRect(aX, aY, aWidth, size);
02097       }
02098       if (decorations & NS_FONT_DECORATION_UNDERLINE) {
02099         aRenderingContext.SetColor(underColor);
02100         aRenderingContext.FillRect(aX, aY + baseline - offset, aWidth, size);
02101       }
02102     }
02103     if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
02104       aTextStyle.mNormalFont->GetStrikeout(offset, size);
02105       aRenderingContext.SetColor(strikeColor);
02106       aRenderingContext.FillRect(aX, aY + baseline - offset, aWidth, size);
02107     }
02108   }
02109 
02110   if (aDetails){
02111     nsRect rect = GetRect();
02112     while(aDetails){
02113       const nscoord* sp= aSpacing;
02114       PRInt32 startOffset = 0;
02115       PRInt32 textWidth = 0;
02116       PRInt32 start = PR_MAX(0,(aDetails->mStart - (PRInt32)aIndex));
02117       PRInt32 end = PR_MIN((PRInt32)aLength,(aDetails->mEnd - (PRInt32)aIndex));
02118       PRInt32 i;
02119       if ((start < end) && ((aLength - start) > 0))
02120       {
02121         //aDetails allready processed to have offsets from frame start not content offsets
02122         if (start < end){
02123           if (aLength == 1)
02124             textWidth = aWidth;
02125           else {
02126             if (aDetails->mStart > 0){
02127               if (sp)
02128               {
02129                 for (i = 0; i < start;i ++){
02130                   startOffset += *sp ++;
02131                 }
02132               }
02133               else
02134                 aRenderingContext.GetWidth(aText, start, startOffset);
02135             }
02136             if (sp){
02137               for (i = start; i < end;i ++){
02138                 textWidth += *sp ++;
02139               }
02140             }
02141             else
02142               aRenderingContext.GetWidth(aText + start,
02143                                            PRUint32(end - start), textWidth);
02144   
02145           }
02146           nscoord offset, size;
02147           nscoord baseline = mAscent;
02148           switch (aDetails->mType)
02149           {
02150           case nsISelectionController::SELECTION_NORMAL:
02151 #if 0
02152             {
02153             //using new selectionpainting now
02154 //
02155 // XOR InvertRect is currently implemented only in the unix and windows
02156 // rendering contexts.  When other platforms implement InvertRect(), they
02157 // can be added here.  Eventually this #ifdef should die.
02158 //
02159 // For platforms that dont implement InvertRect(), the selection will be 
02160 // a non-filled rectangle.
02161 #ifdef USE_INVERT_FOR_SELECTION
02162               aRenderingContext.SetColor(NS_RGB(255,255,255));
02163               aRenderingContext.InvertRect(aX + startOffset, aY, textWidth, rect.height);
02164 #else
02165               aRenderingContext.SetColor(NS_RGB(0,0,0));
02166               aRenderingContext.DrawRect(aX + startOffset, aY, textWidth, rect.height);
02167 #endif
02168             }
02169 #endif //0
02170                                 break;
02171            case nsISelectionController::SELECTION_SPELLCHECK:{
02172               aTextStyle.mNormalFont->GetUnderline(offset, size);
02173               aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
02174               aRenderingContext.SetColor(NS_RGB(255,0,0));
02175               /*
02176                * If the rendering context is drawing text from right to left,
02177                * reverse the coordinates of the underline to match.
02178                */
02179               if (aRightToLeftText) {
02180                 nscoord rightEdge = aX + aWidth;
02181                 aRenderingContext.DrawLine(rightEdge - textWidth - startOffset,
02182                                            aY + baseline - offset,
02183                                            rightEdge - startOffset,
02184                                            aY + baseline - offset);
02185               }
02186               else {
02187                 aRenderingContext.DrawLine(aX + startOffset,
02188                                            aY + baseline - offset,
02189                                            aX + startOffset + textWidth,
02190                                            aY + baseline - offset);
02191               }
02192                                 }break;
02193 
02194 #ifdef NO_INVERT
02195            case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
02196            case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:{
02197               NS_ASSERTION(!aRightToLeftText, 
02198                            "Right-to-left text in IME not handled");
02199               aTextStyle.mNormalFont->GetUnderline(offset, size);
02200               aRenderingContext.SetColor(IME_SELECTED_UNDERLINECOLOR);
02201 #ifdef XP_MACOSX // underline thickness is 2 pixel
02202               aRenderingContext.FillRect(aX + startOffset+size, aY + baseline - offset, textWidth-2*size, 2*size);
02203 #else
02204               aRenderingContext.FillRect(aX + startOffset+size, aY + baseline - offset, textWidth-2*size, size);
02205 #endif
02206                                 }break;
02207            case nsISelectionController::SELECTION_IME_RAWINPUT:
02208            case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:{
02209               NS_ASSERTION(!aRightToLeftText, 
02210                            "Right-to-left text in IME not handled");
02211               aTextStyle.mNormalFont->GetUnderline(offset, size);
02212               aRenderingContext.SetColor(IME_UNDERLINECOLOR);
02213 #ifdef XP_MACOSX // underline thicness is 2 pixel
02214               aRenderingContext.FillRect(aX + startOffset+size, aY + baseline - offset, textWidth-2*size, 2*size);
02215 #else
02216               aRenderingContext.FillRect(aX + startOffset+size, aY + baseline - offset, textWidth-2*size, size);
02217 #endif
02218                                 }break;
02219 // end NO_INVERT part
02220 
02221 #else             
02222            case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:{
02223               NS_ASSERTION(!aRightToLeftText, 
02224                            "Right-to-left text in IME not handled");
02225 #ifdef USE_INVERT_FOR_SELECTION
02226               aRenderingContext.SetColor(NS_RGB(255,255,255));
02227               aRenderingContext.InvertRect(aX + startOffset, aY, textWidth, rect.height);
02228 #else
02229               aRenderingContext.SetColor(NS_RGB(255,255,128));
02230               aRenderingContext.DrawRect(aX + startOffset, aY, textWidth, rect.height);
02231 #endif        
02232               aTextStyle.mNormalFont->GetUnderline(offset, size);
02233               aRenderingContext.SetColor(IME_RAW_COLOR);
02234               aRenderingContext.FillRect(aX + startOffset+size, aY + baseline - offset, textWidth-2*size, size);
02235                                 }break;
02236           case nsISelectionController::SELECTION_IME_RAWINPUT:{
02237               NS_ASSERTION(!aRightToLeftText, 
02238                            "Right-to-left text in IME not handled");
02239               aTextStyle.mNormalFont->GetUnderline(offset, size);
02240               aRenderingContext.SetColor(IME_RAW_COLOR);
02241               aRenderingContext.FillRect(aX + startOffset+size, aY + baseline - offset, textWidth-2*size, size);
02242                                 }break;
02243           case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:{
02244               NS_ASSERTION(!aRightToLeftText, 
02245                            "Right-to-left text in IME not handled");
02246 #ifdef USE_INVERT_FOR_SELECTION
02247               aRenderingContext.SetColor(NS_RGB(255,255,255));
02248               aRenderingContext.InvertRect(aX + startOffset, aY, textWidth, rect.height);
02249 #else
02250               aRenderingContext.SetColor(NS_RGB(255,255,128));
02251               aRenderingContext.DrawRect(aX + startOffset, aY, textWidth, rect.height);
02252 #endif        
02253               aTextStyle.mNormalFont->GetUnderline(offset, size);
02254               aRenderingContext.SetColor(IME_CONVERTED_COLOR);
02255               aRenderingContext.FillRect(aX + startOffset+size, aY + baseline - offset, textWidth-2*size, size);
02256                                 }break;
02257           case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:{
02258               NS_ASSERTION(!aRightToLeftText, 
02259                            "Right-to-left text in IME not handled");
02260               aTextStyle.mNormalFont->GetUnderline(offset, size);
02261               aRenderingContext.SetColor(IME_CONVERTED_COLOR);
02262               aRenderingContext.FillRect(aX + startOffset+size, aY + baseline - offset, textWidth-2*size, size);
02263                                 }break;
02264 #endif
02265           default:
02266             NS_ASSERTION(0,"what type of selection do i not know about?");
02267             break;
02268           }
02269 
02270         }
02271       }
02272       aDetails = aDetails->mNext;
02273     }
02274   }
02275 }
02276 
02277 
02278 
02279 nsresult
02280 nsTextFrame::GetContentAndOffsetsForSelection(nsPresContext *aPresContext, nsIContent **aContent, PRInt32 *aOffset, PRInt32 *aLength)
02281 {
02282   if (!aContent || !aOffset || !aLength)
02283     return NS_ERROR_NULL_POINTER;
02284   //ARE WE GENERATED??
02285   *aContent = nsnull;
02286   *aOffset = mContentOffset;
02287   *aLength = mContentLength;
02288   nsIFrame *parent = GetParent();
02289   if (parent)
02290   {
02291     if ((mState & NS_FRAME_GENERATED_CONTENT) != 0)//parent is generated so so are we.
02292     {
02293       //we COULD check the previous sibling but I dont think that is reliable
02294       *aContent = parent->GetContent();
02295       if(!*aContent)
02296         return NS_ERROR_FAILURE;
02297       NS_ADDREF(*aContent);
02298 
02299       //ARE WE A BEFORE FRAME? if not then we assume we are an after frame. this may be bad later
02300       nsIFrame *grandParent = parent->GetParent();
02301       if (grandParent)
02302       {
02303         nsIFrame *firstParent = grandParent->GetFirstChild(nsnull);
02304         if (firstParent)
02305         {
02306           *aLength = 0;
02307           if (firstParent == parent) //then our parent is the first child of granddad. use BEFORE
02308           {
02309             *aOffset = 0;
02310           }
02311           else
02312           {
02313             *aOffset = (*aContent)->GetChildCount();
02314           }
02315         }
02316         else
02317           return NS_OK;
02318       }
02319     }
02320   }
02321   //END GENERATED BLOCK 
02322   if (!*aContent)
02323   {
02324     *aContent = mContent;
02325     NS_IF_ADDREF(*aContent);
02326   }
02327 
02328   return NS_OK;
02329 }
02330 
02331 //---------------------------------------------------------
02332 nsresult nsTextFrame::GetTextInfoForPainting(nsPresContext*          aPresContext,
02333                                              nsIRenderingContext&     aRenderingContext,
02334                                              nsIPresShell**           aPresShell,
02335                                              nsISelectionController** aSelectionController,
02336                                              PRBool&                  aDisplayingSelection,
02337                                              PRBool&                  aIsPaginated,
02338                                              PRBool&                  aIsSelected,
02339                                              PRBool&                  aHideStandardSelection,
02340                                              PRInt16&                 aSelectionValue,
02341                                              nsILineBreaker**         aLineBreaker)
02342 {
02343   NS_ENSURE_ARG_POINTER(aPresContext);
02344   NS_ENSURE_ARG_POINTER(aPresShell);
02345   NS_ENSURE_ARG_POINTER(aSelectionController);
02346   NS_ENSURE_ARG_POINTER(aLineBreaker);
02347 
02348   //get the presshell
02349   NS_IF_ADDREF(*aPresShell = aPresContext->GetPresShell());
02350   if (!*aPresShell)
02351     return NS_ERROR_FAILURE;
02352 
02353   //get the selection controller
02354   nsresult rv = GetSelectionController(aPresContext, aSelectionController);
02355   if (NS_FAILED(rv) || !(*aSelectionController))
02356     return NS_ERROR_FAILURE;
02357 
02358   aIsPaginated = aPresContext->IsPaginated();
02359 
02360   (*aSelectionController)->GetDisplaySelection(&aSelectionValue);
02361 
02362   if (aIsPaginated) {
02363     aDisplayingSelection = aPresContext->IsRenderingOnlySelection();
02364   } else {
02365     //if greater than hidden then we display some kind of selection
02366     aDisplayingSelection = (aSelectionValue > nsISelectionController::SELECTION_HIDDEN);
02367   }
02368 
02369   PRInt16 textSel=0; 
02370   (*aSelectionController)->GetSelectionFlags(&textSel);
02371   if (!(textSel & nsISelectionDisplay::DISPLAY_TEXT))
02372     aDisplayingSelection = PR_FALSE;
02373 
02374   // the spellcheck selection should be visible all the time
02375   aHideStandardSelection = !aDisplayingSelection;
02376   if (!aDisplayingSelection){
02377     nsCOMPtr<nsISelection> spellcheckSelection;
02378     (*aSelectionController)->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
02379                                           getter_AddRefs(spellcheckSelection));
02380     if (spellcheckSelection){
02381       PRBool iscollapsed = PR_FALSE;
02382       spellcheckSelection->GetIsCollapsed(&iscollapsed);
02383       if (!iscollapsed)
02384         aDisplayingSelection = PR_TRUE;
02385     }
02386   }
02387 
02388   // Transform text from content into renderable form
02389   // XXX If the text fragment is already Unicode and the text wasn't
02390   // transformed when we formatted it, then there's no need to do all
02391   // this and we should just render the text fragment directly. See
02392   // PaintAsciiText()...
02393   nsIDocument *doc = (*aPresShell)->GetDocument();
02394   if (!doc)
02395     return NS_ERROR_FAILURE;
02396 
02397   NS_IF_ADDREF(*aLineBreaker = doc->GetLineBreaker());
02398 
02399   aIsSelected = (GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
02400 
02401   return NS_OK;
02402 }
02403 
02404 PRBool
02405 nsTextFrame::IsTextInSelection(nsPresContext* aPresContext,
02406                                nsIRenderingContext& aRenderingContext)
02407 {
02408   nsCOMPtr<nsISelectionController> selCon;
02409   nsCOMPtr<nsIPresShell> shell;
02410   PRBool  displaySelection;
02411   PRBool  isPaginated;
02412   PRBool  isSelected;
02413   PRBool  hideStandardSelection;
02414   PRInt16 selectionValue;
02415   nsCOMPtr<nsILineBreaker> lb;
02416   if (NS_FAILED(GetTextInfoForPainting(aPresContext, 
02417                                        aRenderingContext,
02418                                        getter_AddRefs(shell),
02419                                        getter_AddRefs(selCon),
02420                                        displaySelection,
02421                                        isPaginated,
02422                                        isSelected,
02423                                        hideStandardSelection,
02424                                        selectionValue,
02425                                        getter_AddRefs(lb)))) {
02426     return PR_FALSE;
02427   }
02428 
02429   // Make enough space to transform
02430   nsAutoTextBuffer paintBuffer;
02431   nsAutoIndexBuffer indexBuffer;
02432   if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) {
02433     return PR_FALSE;
02434   }
02435   TextPaintStyle ts(aPresContext, aRenderingContext, mStyleContext);
02436 
02437   // Transform text from content into renderable form
02438   // XXX If the text fragment is already Unicode and the text wasn't
02439   // transformed when we formatted it, then there's no need to do all
02440   // this and we should just render the text fragment directly. See
02441   // PaintAsciiText()...
02442 
02443   nsTextTransformer tx(lb, nsnull, aPresContext);
02444   PRInt32 textLength;
02445   // no need to worry about justification, that's always on the slow path
02446   PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
02447 
02448   PRInt32* ip     = indexBuffer.mBuffer;
02449   PRUnichar* text = paintBuffer.mBuffer;
02450 
02451   isSelected = PR_FALSE;
02452   if (0 != textLength) {
02453 
02454     SelectionDetails *details = nsnull;
02455     nsCOMPtr<nsIFrameSelection> frameSelection;
02456     //get the frameSelection from the selection controller
02457     if (selCon) {
02458       frameSelection = do_QueryInterface(selCon); //this MAY implement
02459     }
02460     nsresult rv = NS_OK;
02461     //if that failed get it from the pres shell
02462     if (!frameSelection)
02463       frameSelection = shell->FrameSelection();
02464 
02465     nsCOMPtr<nsIContent> content;
02466     PRInt32 offset;
02467     PRInt32 length;
02468 
02469     rv = GetContentAndOffsetsForSelection(aPresContext,
02470                                           getter_AddRefs(content),
02471                                           &offset, &length);
02472     if (NS_SUCCEEDED(rv) && content) {
02473       rv = frameSelection->LookUpSelection(content, mContentOffset,
02474                                            mContentLength, &details,
02475                                            PR_FALSE);
02476     }
02477       
02478     //where are the selection points "really"
02479     SelectionDetails *sdptr = details;
02480     while (sdptr){
02481       sdptr->mStart = ip[sdptr->mStart] - mContentOffset;
02482       sdptr->mEnd = ip[sdptr->mEnd]  - mContentOffset;
02483       sdptr = sdptr->mNext;
02484     }
02485     //while we have substrings...
02486     //PRBool drawn = PR_FALSE;
02487     DrawSelectionIterator iter(content, details,text,(PRUint32)textLength, ts, nsISelectionController::SELECTION_NORMAL, aPresContext, mStyleContext);
02488     if (!iter.IsDone() && iter.First()) {
02489       isSelected = PR_TRUE;
02490     }
02491 
02492     sdptr = details;
02493     if (details) {
02494       while ((sdptr = details->mNext) != nsnull) {
02495         delete details;
02496         details = sdptr;
02497       }
02498       delete details;
02499     }
02500   }
02501   return isSelected;
02502 }
02503 
02504 NS_IMETHODIMP
02505 nsTextFrame::IsVisibleForPainting(nsPresContext *     aPresContext, 
02506                                   nsIRenderingContext& aRenderingContext,
02507                                   PRBool               aCheckVis,
02508                                   PRBool*              aIsVisible)
02509 {
02510   if (aCheckVis) {
02511     if (!GetStyleVisibility()->IsVisible()) {
02512       *aIsVisible = PR_FALSE;
02513       return NS_OK;
02514     }
02515   }
02516 
02517   // Start by assuming we are visible and need to be painted
02518   PRBool isVisible = PR_TRUE;
02519 
02520   if (aPresContext->IsPaginated()) {
02521     if (aPresContext->IsRenderingOnlySelection()) {
02522       // Check the quick way first
02523       PRBool isSelected = (mState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
02524       if (isSelected) {
02525         isVisible = IsTextInSelection(aPresContext, aRenderingContext);
02526       } else {
02527         isVisible = PR_FALSE;
02528       }
02529     }
02530   } 
02531 
02532   *aIsVisible = isVisible;
02533 
02534   return NS_OK;
02535 }
02536 
02537 void
02538 nsTextFrame::PaintUnicodeText(nsPresContext* aPresContext,
02539                               nsIRenderingContext& aRenderingContext,
02540                               nsStyleContext* aStyleContext,
02541                               TextPaintStyle& aTextStyle,
02542                               nscoord dx, nscoord dy)
02543 {
02544   nsCOMPtr<nsISelectionController> selCon;
02545   nsCOMPtr<nsIPresShell> shell;
02546   PRBool  displaySelection,canDarkenColor=PR_FALSE;
02547   PRBool  isPaginated;
02548   PRBool  isSelected;
02549   PRBool hideStandardSelection;
02550   PRInt16 selectionValue;
02551   nsCOMPtr<nsILineBreaker> lb;
02552 #ifdef IBMBIDI
02553   PRBool  isOddLevel = PR_FALSE;
02554 #endif
02555 
02556   if (NS_FAILED(GetTextInfoForPainting(aPresContext, 
02557                                        aRenderingContext,
02558                                        getter_AddRefs(shell),
02559                                        getter_AddRefs(selCon),
02560                                        displaySelection,
02561                                        isPaginated,
02562                                        isSelected,
02563                                        hideStandardSelection,
02564                                        selectionValue,
02565                                        getter_AddRefs(lb)))) {
02566      return;
02567   }
02568 
02569   if(isPaginated){
02570     canDarkenColor = CanDarken(aPresContext);
02571   }
02572 
02573   // Make enough space to transform
02574   nsAutoTextBuffer paintBuffer;
02575   nsAutoIndexBuffer indexBuffer;
02576   if (displaySelection) {
02577     if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) {
02578       return;
02579     }
02580   }
02581   nscoord width = mRect.width;
02582 
02583   // Transform text from content into renderable form
02584   // XXX If the text fragment is already Unicode and the text wasn't
02585   // transformed when we formatted it, then there's no need to do all
02586   // this and we should just render the text fragment directly. See
02587   // PaintAsciiText()...
02588 
02589   nsTextTransformer tx(lb, nsnull, aPresContext);
02590   PRInt32 textLength;
02591   // no need to worry about justification, that's always on the slow path
02592   PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull),
02593                      &paintBuffer, &textLength);
02594 
02595   PRInt32* ip = indexBuffer.mBuffer;
02596   PRUnichar* text = paintBuffer.mBuffer;
02597 
02598   if (0 != textLength) 
02599   {
02600 #ifdef IBMBIDI
02601     PRBool isRightToLeftOnBidiPlatform = PR_FALSE;
02602     PRBool isBidiSystem = PR_FALSE;
02603     nsCharType charType = eCharType_LeftToRight;
02604     if (aPresContext->BidiEnabled()) {
02605       isBidiSystem = aPresContext->IsBidiSystem();
02606       isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
02607       charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsLayoutAtoms::charType));
02608 
02609       isRightToLeftOnBidiPlatform = (isBidiSystem &&
02610                                      (eCharType_RightToLeft == charType ||
02611                                       eCharType_RightToLeftArabic == charType));
02612       if (isRightToLeftOnBidiPlatform) {
02613         // indicate that the platform should use its native
02614         // capabilities to reorder the text with right-to-left
02615         // base direction 
02616         aRenderingContext.SetRightToLeftText(PR_TRUE);
02617       }
02618       nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils();
02619       if (bidiUtils) {
02620 #ifdef DEBUG
02621         PRInt32 rememberTextLength = textLength;
02622 #endif
02623         bidiUtils->ReorderUnicodeText(text, textLength,
02624                                       charType, isOddLevel, isBidiSystem);
02625         NS_ASSERTION(rememberTextLength == textLength, "Bidi formatting changed text length");
02626       }
02627     }
02628 #endif // IBMBIDI
02629     if (!displaySelection || !isSelected ) //draw text normally
02630     { 
02631       // When there is no selection showing, use the fastest and
02632       // simplest rendering approach
02633 
02634       aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
02635       aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
02636       PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
02637                            aTextStyle, dx, dy, width, PR_FALSE);
02638     }
02639     else 
02640     { //we draw according to selection rules
02641       SelectionDetails *details = nsnull;
02642       nsCOMPtr<nsIFrameSelection> frameSelection;
02643       //get the frameSelection from the selection controller
02644       if (selCon)
02645       {
02646         frameSelection = do_QueryInterface(selCon); //this MAY implement
02647       }
02648       //if that failed get it from the pres shell
02649       nsresult rv = NS_OK;
02650       if (!frameSelection)
02651         frameSelection = shell->FrameSelection();
02652       nsCOMPtr<nsIContent> content;
02653       PRInt32 offset;
02654       PRInt32 length;
02655 
02656       rv = GetContentAndOffsetsForSelection(aPresContext,
02657                                             getter_AddRefs(content),
02658                                             &offset, &length);
02659       if (NS_SUCCEEDED(rv) && content) {
02660         rv = frameSelection->LookUpSelection(content, mContentOffset, 
02661                                              mContentLength, &details,
02662                                              PR_FALSE);
02663       }
02664         
02665       //where are the selection points "really"
02666       SelectionDetails *sdptr = details;
02667       while (sdptr){
02668         sdptr->mStart = ip[sdptr->mStart] - mContentOffset;
02669         sdptr->mEnd = ip[sdptr->mEnd]  - mContentOffset;
02670 #ifdef SUNCTL
02671         nsCOMPtr<nsILE> ctlObj;
02672         ctlObj = do_CreateInstance(kLECID, &rv);
02673         if (NS_FAILED(rv)) {
02674           NS_WARNING("Cell based cursor movement will not be supported\n");
02675           ctlObj = nsnull;
02676         }
02677         else {
02678           PRInt32 start, end;
02679           PRBool  needsCTL = PR_FALSE;
02680 
02681           ctlObj->NeedsCTLFix(text, sdptr->mStart, sdptr->mEnd, &needsCTL);
02682 
02683           if (needsCTL && (sdptr->mEnd < textLength)) {
02684             ctlObj->GetRangeOfCluster(text, PRInt32(textLength), sdptr->mEnd,
02685                                       &start, &end);
02686             if (sdptr->mStart > sdptr->mEnd) /* Left Edge */
02687               sdptr->mEnd = start;
02688             else
02689               sdptr->mEnd = end;
02690           }
02691 
02692           /* Always start selection from a Right Edge */
02693           if (needsCTL && (sdptr->mStart > 0)) {
02694             ctlObj->GetRangeOfCluster(text, PRInt32(textLength),
02695                                       sdptr->mStart, &start, &end);
02696             sdptr->mStart = end;
02697           }
02698         }
02699 #endif /* SUNCTL */
02700 #ifdef IBMBIDI
02701         AdjustSelectionPointsForBidi(sdptr, textLength, CHARTYPE_IS_RTL(charType), isOddLevel, isBidiSystem);
02702 #endif
02703         sdptr = sdptr->mNext;
02704       }
02705       if (!hideStandardSelection || displaySelection) {
02706       /*
02707        * Text is drawn by drawing the entire string every time, but
02708        * using clip regions to control which part of the text is shown
02709        * (selected or unselected.)  We do this because you can't
02710        * assume that the layout of a part of text will be the same
02711        * when it's drawn apart from the entire string.  This is true
02712        * in languages like arabic, where shaping affects entire words.
02713        * Simply put: length("abcd") != length("ab") + length("cd") in
02714        * some languages.
02715        */
02716 
02717       // See if this rendering backend supports getting cluster
02718       // information.
02719       PRUint32 clusterHint = 0;
02720       aRenderingContext.GetHints(clusterHint);
02721       clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
02722 
02723       //while we have substrings...
02724       //PRBool drawn = PR_FALSE;
02725       DrawSelectionIterator iter(content, details,text,(PRUint32)textLength,aTextStyle, selectionValue, aPresContext, mStyleContext);
02726       if (!iter.IsDone() && iter.First())
02727       {
02728         nscoord currentX = dx;
02729         nscoord newWidth;//temp
02730 #ifdef IBMBIDI // Simon - display substrings RTL in RTL frame
02731         nscoord FrameWidth = 0;
02732         if (isRightToLeftOnBidiPlatform)
02733           if (NS_SUCCEEDED(aRenderingContext.GetWidth(text, textLength, FrameWidth)))
02734             currentX = dx + FrameWidth;
02735 #endif
02736         while (!iter.IsDone())
02737         {
02738           PRUnichar *currenttext  = iter.CurrentTextUnicharPtr();
02739           PRUint32   currentlength= iter.CurrentLength();
02740           //TextStyle &currentStyle = iter.CurrentStyle();
02741           nscolor    currentFGColor, currentBKColor;
02742           PRBool     isCurrentBKColorTransparent;
02743 
02744           PRBool     isSelection = iter.GetSelectionColors(&currentFGColor,
02745                                                            &currentBKColor,
02746                                                            &isCurrentBKColorTransparent);
02747 
02748           if (currentlength > 0)
02749           {
02750             if (clusterHint) {
02751               PRUint32 tmpWidth;
02752               rv = aRenderingContext.GetRangeWidth(text, textLength, currenttext - text,
02753                                                    (currenttext - text) + currentlength,
02754                                                    tmpWidth);
02755               newWidth = nscoord(tmpWidth);
02756             }
02757             else {
02758               rv = aRenderingContext.GetWidth(currenttext, currentlength,newWidth); //ADJUST FOR CHAR SPACING
02759             }
02760             if (NS_SUCCEEDED(rv)) {
02761               if (isRightToLeftOnBidiPlatform)
02762                 currentX -= newWidth;
02763               if (isSelection && !isPaginated)
02764               {//DRAW RECT HERE!!!
02765                 if (!isCurrentBKColorTransparent) {
02766                   aRenderingContext.SetColor(currentBKColor);
02767                   aRenderingContext.FillRect(currentX, dy, newWidth, mRect.height);
02768                 }
02769              }
02770             }
02771             else {
02772               newWidth = 0;
02773             }
02774           }
02775           else {
02776             newWidth = 0;
02777           }
02778 
02779           aRenderingContext.PushState();
02780 
02781           nsRect rect(currentX, dy, newWidth, mRect.height);
02782           aRenderingContext.SetClipRect(rect, nsClipCombine_kIntersect);
02783                       
02784           if (isPaginated && !iter.IsBeforeOrAfter()) {
02785             aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
02786             aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
02787           } else if (!isPaginated) {
02788             aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor,canDarkenColor));
02789             aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
02790           }
02791 
02792           aRenderingContext.PopState();
02793 
02794 #ifdef IBMBIDI
02795           if (!isRightToLeftOnBidiPlatform)
02796 #endif
02797           currentX += newWidth; // increment twips X start
02798 
02799           iter.Next();
02800         }
02801       }
02802       else if (!isPaginated) 
02803       {
02804         aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
02805         aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
02806       }
02807       }
02808       PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
02809                            aTextStyle, dx, dy, width,
02810                            isRightToLeftOnBidiPlatform, text, details, 0,
02811                            (PRUint32)textLength, nsnull);
02812       sdptr = details;
02813       if (details){
02814         while ((sdptr = details->mNext) != nsnull) {
02815           delete details;
02816           details = sdptr;
02817         }
02818         delete details;
02819       }
02820     }
02821 #ifdef IBMBIDI
02822     if (isRightToLeftOnBidiPlatform) {
02823       // indicate that future text should not be reordered with
02824       // right-to-left base direction 
02825       aRenderingContext.SetRightToLeftText(PR_FALSE);
02826     }
02827 #endif // IBMBIDI
02828   }
02829 }
02830 
02831 //measure Spaced Textvoid
02832 nsresult
02833 nsTextFrame::GetPositionSlowly(nsPresContext* aPresContext,
02834                                nsIRenderingContext* aRendContext,
02835                                const nsPoint& aPoint,
02836                                nsIContent** aNewContent,
02837                                PRInt32& aOffset)
02838 
02839 {
02840   // pre-condition tests
02841   NS_PRECONDITION(aPresContext && aRendContext && aNewContent, "null arg");
02842   if (!aPresContext || !aRendContext || !aNewContent) {
02843     return NS_ERROR_NULL_POINTER;
02844   }
02845   // initialize out param
02846   *aNewContent = nsnull;
02847 
02848   TextStyle ts(aPresContext, *aRendContext, mStyleContext);
02849   if (!ts.mSmallCaps && !ts.mWordSpacing && !ts.mLetterSpacing && !ts.mJustifying) {
02850     return NS_ERROR_INVALID_ARG;
02851   }
02852   nsIView * view;
02853   nsPoint origin;
02854   GetOffsetFromView(origin, &view);
02855 
02856   /* This if clause is the cause of much pain.  If aNewContent is set, then any
02857    * code path that returns an error must set aNewContent to null before returning,
02858    * or risk the caller unknowingly decrementing aNewContent inappropriately.
02859    * Here's what Robert O'Callahan has to say on the matter:
02860         If I'm not mistaken, in GetPositionSlowly, the values of aNewContent and
02861         aOffset set in the conditional "if (aPoint.x - origin.x < 0)" are
02862         overwritten on all successful return paths. Since they should never be
02863         used by the caller if the function fails, that entire "if" statement is
02864         --- or should be --- a no-op. Come to think of it, it doesn't make sense
02865         either; setting aOffset to zero is nonsense.
02866 
02867         I recommend you just delete that "if" statement.
02868    * 
02869    * If this clause is removed, then some of the bullet-proofing code
02870    * prefaced with "bug 56704" comments can be removed as well.
02871    */
02872   if (aPoint.x - origin.x < 0)
02873   {
02874       *aNewContent = mContent;
02875       aOffset =0;
02876   }
02877   nsIDocument *doc = GetDocument(aPresContext);
02878 
02879   // Make enough space to transform
02880   nsAutoTextBuffer paintBuffer;
02881   nsAutoIndexBuffer indexBuffer;
02882   nsresult rv = indexBuffer.GrowTo(mContentLength + 1);
02883   if (NS_FAILED(rv)) {
02884     // If we've already assigned aNewContent, make sure to 0 it out here.
02885     // See bug 56704.
02886     *aNewContent = nsnull;
02887     return rv;
02888   }
02889 
02890   // Transform text from content into renderable form
02891   nsTextTransformer tx(doc->GetLineBreaker(), nsnull, aPresContext);
02892   PRInt32 textLength;
02893   PRIntn numJustifiableCharacter;
02894 
02895   PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength, PR_TRUE, &numJustifiableCharacter);
02896   if (textLength <= 0) {
02897     // If we've already assigned aNewContent, make sure to 0 it out here.
02898     // aNewContent is undefined in the case that we return a failure,
02899     // If we were to return a valid pointer,  we risk decrementing that node's 
02900     // ref count an extra time by the caller.  
02901     // See bug 56704 for more details.
02902     *aNewContent = nsnull;
02903     return NS_ERROR_FAILURE;
02904   }
02905 
02906 #ifdef IBMBIDI // Simon -- reverse RTL text here
02907   PRBool isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
02908   if (isOddLevel) {
02909     PRUnichar *tStart, *tEnd;
02910     PRUnichar tSwap;
02911     for (tStart = paintBuffer.mBuffer, tEnd = tStart + textLength - 1; tEnd > tStart; tStart++, tEnd--) {
02912       tSwap = *tStart;
02913       *tStart = *tEnd;
02914       *tEnd = tSwap;
02915     }
02916   }
02917 #endif // IBMBIDI
02918 
02919   ComputeExtraJustificationSpacing(*aRendContext, ts, paintBuffer.mBuffer, textLength, numJustifiableCharacter);
02920 
02921 //IF STYLE SAYS TO SELECT TO END OF FRAME HERE...
02922   PRInt32 prefInt =
02923     nsContentUtils::GetIntPref("browser.drag_out_of_frame_style");
02924 
02925   PRBool outofstylehandled = PR_FALSE;
02926 
02927   if (prefInt)
02928   {
02929     if (aPoint.y < origin.y)//above rectangle
02930     {
02931       aOffset = mContentOffset;
02932       outofstylehandled = PR_TRUE;
02933     }
02934     else if ((aPoint.y - origin.y) > mRect.height)
02935     {
02936       aOffset = mContentOffset + mContentLength;
02937       outofstylehandled = PR_TRUE;
02938     }
02939   }
02940 
02941   if (!outofstylehandled) //then we drag to closest X point and dont worry about the 'Y'
02942 //END STYLE RULE
02943   {
02944     //the following will first get the index into the PAINTBUFFER then the actual content
02945     nscoord adjustedX = PR_MAX(0,aPoint.x-origin.x);
02946 
02947 #ifdef IBMBIDI
02948     if (isOddLevel)
02949       aOffset = mContentOffset + textLength -
02950                 GetLengthSlowly(*aRendContext, ts, paintBuffer.mBuffer,
02951                                 textLength, PR_TRUE, adjustedX);
02952     else
02953 #endif
02954     aOffset = mContentOffset +
02955               GetLengthSlowly(*aRendContext, ts,paintBuffer.mBuffer,
02956                               textLength, PR_TRUE, adjustedX);
02957     PRInt32 i;
02958     PRInt32* ip = indexBuffer.mBuffer;
02959     for (i = 0;i <= mContentLength; i ++){
02960       if ((ip[i] >= aOffset) && //reverse mapping
02961           (! IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) {
02962           aOffset = i + mContentOffset;
02963           break;
02964       }
02965     }
02966   }
02967 
02968   *aNewContent = mContent;
02969   if (*aNewContent)
02970     (*aNewContent)->AddRef();
02971   return NS_OK;
02972 }
02973 
02974 void
02975 nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext,
02976                           nsStyleContext* aStyleContext,
02977                           nsPresContext* aPresContext,
02978                           TextPaintStyle& aTextStyle,
02979                           PRBool aRightToLeftText,
02980                           PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
02981                           nscoord aX, nscoord aY,
02982                           nscoord aWidth, 
02983                           SelectionDetails *aDetails /*=nsnull*/)
02984 {
02985   PRUnichar buf[TEXT_BUF_SIZE];
02986   PRUnichar* bp0 = buf;
02987 
02988   nscoord spacingMem[TEXT_BUF_SIZE];
02989   nscoord* sp0 = spacingMem; 
02990   
02991   PRBool spacing = (0 != aTextStyle.mLetterSpacing) ||
02992     (0 != aTextStyle.mWordSpacing) || aTextStyle.mJustifying;
02993 
02994   PRBool justifying = aTextStyle.mJustifying &&
02995     (aTextStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aTextStyle.mExtraSpacePerJustifiableCharacter != 0);
02996 
02997   PRBool isCJ = IsChineseJapaneseLangGroup();
02998   PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState);
02999 
03000   //German 0x00df might expand to "SS", but no need to count it for speed reason
03001   if (aTextStyle.mSmallCaps) {
03002      if (aLength*2 > TEXT_BUF_SIZE) {
03003        bp0 = new PRUnichar[aLength*2];
03004        if (spacing)
03005          sp0 = new nscoord[aLength*2];
03006      }
03007   }
03008   else if (aLength > TEXT_BUF_SIZE) {
03009     bp0 = new PRUnichar[aLength];
03010     if (spacing)
03011       sp0 = new nscoord[aLength];
03012   }
03013 
03014   PRUnichar* bp = bp0;
03015   nscoord* sp = sp0;
03016 
03017   nsIFontMetrics* lastFont = aTextStyle.mLastFont;
03018   PRInt32 pendingCount;
03019   PRUnichar* runStart = bp;
03020   nscoord charWidth, width = 0;
03021   PRInt32 countSoFar = 0;
03022   // Save the color we want to use for the text, since calls to
03023   // PaintTextDecorations in this method will call SetColor() on the rendering
03024   // context.
03025   nscolor textColor;
03026   aRenderingContext.GetColor(textColor);
03027   for (; --aLength >= 0; aBuffer++) {
03028     nsIFontMetrics* nextFont;
03029     nscoord glyphWidth = 0;
03030     PRUnichar ch = *aBuffer;
03031     if (aTextStyle.mSmallCaps &&
03032         (IsLowerCase(ch) || (ch == kSZLIG))) {
03033       nextFont = aTextStyle.mSmallFont;
03034     }
03035     else {
03036       nextFont = aTextStyle.mNormalFont;
03037     }
03038     if (nextFont != lastFont) {
03039       pendingCount = bp - runStart;
03040       if (0 != pendingCount) {
03041         // Render the text with the color specified first.
03042         aRenderingContext.SetColor(textColor);
03043         // Measure previous run of characters using the previous font
03044         aRenderingContext.DrawString(runStart, pendingCount,
03045                                      aX, aY + mAscent, -1,
03046                                      spacing ? sp0 : nsnull);
03047 
03048         // Note: use aY not small-y so that decorations are drawn with
03049         // respect to the normal-font not the current font.
03050         PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
03051                              aTextStyle, aX, aY, width,
03052                              aRightToLeftText, runStart, aDetails,
03053                              countSoFar, pendingCount, spacing ? sp0 : nsnull);
03054         countSoFar += pendingCount;
03055         aWidth -= width;
03056         aX += width;
03057         runStart = bp = bp0;
03058         sp = sp0;
03059         width = 0;
03060       }
03061       aRenderingContext.SetFont(nextFont);
03062       lastFont = nextFont;
03063     }
03064     if (nextFont == aTextStyle.mSmallFont) {
03065       PRUnichar upper_ch;
03066       // German szlig should be expanded to "SS".
03067       if (ch == kSZLIG)
03068         upper_ch = (PRUnichar)'S';
03069       else
03070         upper_ch = ToUpperCase(ch);
03071       aRenderingContext.GetWidth(upper_ch, charWidth);
03072       glyphWidth += charWidth + aTextStyle.mLetterSpacing;
03073       if (ch == kSZLIG)   //add an additional 'S' here.
03074       {
03075         *bp++ = upper_ch;
03076         if (spacing)
03077           *sp++ = glyphWidth;
03078         width += glyphWidth;
03079       }
03080       ch = upper_ch;
03081     }
03082     else if (ch == ' ') {
03083       glyphWidth += aTextStyle.mSpaceWidth + aTextStyle.mWordSpacing + aTextStyle.mLetterSpacing;
03084     }
03085     else if (IS_HIGH_SURROGATE(ch) && aLength > 0 &&
03086            IS_LOW_SURROGATE(*(aBuffer+1))) {
03087       
03088       // special handling for surrogate pair
03089       aRenderingContext.GetWidth(aBuffer, 2, charWidth);
03090       glyphWidth += charWidth + aTextStyle.mLetterSpacing;
03091       // copy the surrogate low
03092       *bp++ = ch;
03093       --aLength;
03094       aBuffer++;
03095       ch = *aBuffer;
03096       // put the width into the space buffer
03097       width += glyphWidth;
03098       if (spacing)
03099         *sp++ = glyphWidth;
03100       // set the glyphWidth to 0 so the code later will 
03101       // set a 0 for one element in space array for surrogate low to 0
03102       glyphWidth = 0;
03103     }
03104     else {
03105       aRenderingContext.GetWidth(ch, charWidth);
03106       glyphWidth += charWidth + aTextStyle.mLetterSpacing;
03107     }
03108     if (justifying && (!isEndOfLine || aLength > 0)
03109         && IsJustifiableCharacter(ch, isCJ)) {
03110       glyphWidth += aTextStyle.mExtraSpacePerJustifiableCharacter;
03111       if ((PRUint32)--aTextStyle.mNumJustifiableCharacterToRender
03112             < (PRUint32)aTextStyle.mNumJustifiableCharacterReceivingExtraJot) {
03113         glyphWidth++;
03114       }
03115     }
03116     *bp++ = ch;
03117     if (spacing)
03118       *sp++ = glyphWidth;
03119     width += glyphWidth;
03120   }
03121   pendingCount = bp - runStart;
03122   if (0 != pendingCount) {
03123     // Render the text with the color specified first.
03124     aRenderingContext.SetColor(textColor);
03125     // Measure previous run of characters using the previous font
03126     aRenderingContext.DrawString(runStart, pendingCount, aX, aY + mAscent, -1,
03127                                  spacing ? sp0 : nsnull);
03128 
03129     // Note: use aY not small-y so that decorations are drawn with
03130     // respect to the normal-font not the current font.
03131     PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
03132                          aTextStyle, aX, aY, aWidth,
03133                          aRightToLeftText, runStart, aDetails,
03134                          countSoFar, pendingCount, spacing ? sp0 : nsnull);
03135   }
03136   aTextStyle.mLastFont = lastFont;
03137 
03138   if (bp0 != buf) {
03139     delete [] bp0;
03140   }
03141   if (sp0 != spacingMem) {
03142     delete [] sp0;
03143   }
03144 }
03145 
03146 inline void
03147 nsTextFrame::MeasureSmallCapsText(const nsHTMLReflowState& aReflowState,
03148                                   TextStyle& aTextStyle,
03149                                   PRUnichar* aWord,
03150                                   PRInt32 aWordLength,
03151                                   PRBool aIsEndOfFrame,
03152                                   nsTextDimensions* aDimensionsResult)
03153 {
03154   nsIRenderingContext& rc = *aReflowState.rendContext;
03155   aDimensionsResult->Clear();
03156   GetTextDimensions(rc, aTextStyle, aWord, aWordLength, aIsEndOfFrame, aDimensionsResult);
03157   if (aTextStyle.mLastFont != aTextStyle.mNormalFont) {
03158     rc.SetFont(aTextStyle.mNormalFont);
03159     aTextStyle.mLastFont = aTextStyle.mNormalFont;
03160   }
03161 }
03162 
03163 
03164 PRInt32
03165 nsTextFrame::GetTextDimensionsOrLength(nsIRenderingContext& aRenderingContext,
03166                 TextStyle& aStyle,
03167                 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
03168                 nsTextDimensions* aDimensionsResult,
03169                 PRBool aGetTextDimensions/* true=get dimensions false = return length up to aDimensionsResult.width size*/)
03170 {
03171   PRUnichar *inBuffer = aBuffer;
03172   PRInt32 length = aLength;
03173   nsAutoTextBuffer dimensionsBuffer;
03174   if (NS_FAILED(dimensionsBuffer.GrowTo(length))) {
03175     aDimensionsResult->Clear();
03176     return 0;
03177   }
03178   PRUnichar* bp = dimensionsBuffer.mBuffer;
03179 
03180   nsIFontMetrics* lastFont = aStyle.mLastFont;
03181   nsTextDimensions sum, glyphDimensions;
03182   PRBool justifying = aStyle.mJustifying &&
03183     (aStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aStyle.mExtraSpacePerJustifiableCharacter != 0);
03184   PRBool isCJ = IsChineseJapaneseLangGroup();
03185   PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState);
03186 
03187   for (PRInt32 prevLength = length; --length >= 0; prevLength = length) {
03188     PRUnichar ch = *inBuffer++;
03189     if (aStyle.mSmallCaps &&
03190         (IsLowerCase(ch) || (ch == kSZLIG))) {
03191       PRUnichar upper_ch;
03192       // German szlig should be expanded to "SS".
03193       if (ch == kSZLIG)
03194         upper_ch = (PRUnichar)'S';
03195       else
03196         upper_ch = ToUpperCase(ch);
03197       if (lastFont != aStyle.mSmallFont) {
03198         lastFont = aStyle.mSmallFont;
03199         aRenderingContext.SetFont(lastFont);
03200       }
03201       aRenderingContext.GetTextDimensions(&upper_ch, (PRUint32)1, glyphDimensions);
03202       glyphDimensions.width += aStyle.mLetterSpacing;
03203       if (ch == kSZLIG)
03204         glyphDimensions.width += glyphDimensions.width;
03205     }
03206     else if (ch == ' ') {
03207       glyphDimensions.width = aStyle.mSpaceWidth + aStyle.mLetterSpacing
03208         + aStyle.mWordSpacing;
03209     }
03210     else {
03211       if (lastFont != aStyle.mNormalFont) {
03212         lastFont = aStyle.mNormalFont;
03213         aRenderingContext.SetFont(lastFont);
03214       }
03215       if (IS_HIGH_SURROGATE(ch) && length > 0 &&
03216         IS_LOW_SURROGATE(*inBuffer)) {
03217         aRenderingContext.GetTextDimensions(inBuffer-1, (PRUint32)2, glyphDimensions);
03218         length--;
03219         inBuffer++;
03220       } else {
03221         aRenderingContext.GetTextDimensions(&ch, (PRUint32)1, glyphDimensions);
03222       }
03223       glyphDimensions.width += aStyle.mLetterSpacing;
03224     }
03225     if (justifying && (!isEndOfLine || length > 0)
03226         && IsJustifiableCharacter(ch, isCJ)) {
03227       glyphDimensions.width += aStyle.mExtraSpacePerJustifiableCharacter;
03228       if ((PRUint32)--aStyle.mNumJustifiableCharacterToMeasure
03229             < (PRUint32)aStyle.mNumJustifiableCharacterReceivingExtraJot) {
03230         ++glyphDimensions.width;
03231       }
03232     }
03233     sum.Combine(glyphDimensions);
03234     *bp++ = ch;
03235     if (!aGetTextDimensions && sum.width >= aDimensionsResult->width) {
03236       PRInt32 result = aLength - length;
03237       if (2*(sum.width - aDimensionsResult->width) > glyphDimensions.width) //then we have gone too far, back up 1
03238         result = aLength - prevLength;
03239       aStyle.mLastFont = lastFont;
03240       return result;
03241     }
03242   }
03243   aStyle.mLastFont = lastFont;
03244   *aDimensionsResult = sum;
03245   return aLength;
03246 }
03247 
03248 
03249 // XXX factor in logic from RenderString into here; gaps, justification, etc.
03250 void
03251 nsTextFrame::GetTextDimensions(nsIRenderingContext& aRenderingContext,
03252                       TextStyle& aTextStyle,
03253                       PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
03254                       nsTextDimensions* aDimensionsResult)
03255 {
03256   GetTextDimensionsOrLength(aRenderingContext,aTextStyle,
03257                             aBuffer,aLength,aIsEndOfFrame,aDimensionsResult,PR_TRUE);
03258 }
03259 
03260 PRInt32 
03261 nsTextFrame::GetLengthSlowly(nsIRenderingContext& aRenderingContext,
03262                 TextStyle& aStyle,
03263                 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
03264                 nscoord aWidth)
03265 {
03266   nsTextDimensions dimensions;
03267   dimensions.width = aWidth;
03268   return GetTextDimensionsOrLength(aRenderingContext,aStyle,
03269                                    aBuffer,aLength,aIsEndOfFrame,&dimensions,PR_FALSE);
03270 }
03271 
03272 void
03273 nsTextFrame::ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext,
03274                                               TextStyle& aTextStyle,
03275                                               PRUnichar* aBuffer, PRInt32 aLength,
03276                                               PRInt32 aNumJustifiableCharacter)
03277 {
03278   if (aTextStyle.mJustifying) {
03279     nsTextDimensions trueDimensions;
03280     
03281     // OK, so this is a bit ugly. The problem is that to get the right margin
03282     // nice and clean, we have to apply a little extra space to *some* of the
03283     // justifiable characters. It has to be the same ones every time or things will go haywire.
03284     // This implies that the GetTextDimensionsOrLength and RenderString functions depend
03285     // on a little bit of secret state: which part of the prepared text they are
03286     // looking at. It turns out that they get called in a regular way: they look
03287     // at the text from the beginning to the end. So we just count which justifiable character
03288     // we're up to, for each context.
03289     // This is not a great solution, but a perfect solution requires much more
03290     // widespread changes, to explicitly annotate all the transformed text fragments
03291     // that are passed around with their position in the transformed text
03292     // for the entire frame.
03293     aTextStyle.mNumJustifiableCharacterToMeasure = 0;
03294     aTextStyle.mExtraSpacePerJustifiableCharacter = 0;
03295     aTextStyle.mNumJustifiableCharacterReceivingExtraJot = 0;
03296     
03297     GetTextDimensions(aRenderingContext, aTextStyle, aBuffer, aLength, PR_TRUE, &trueDimensions);
03298 
03299     aTextStyle.mNumJustifiableCharacterToMeasure = aNumJustifiableCharacter;
03300     aTextStyle.mNumJustifiableCharacterToRender = aNumJustifiableCharacter;
03301 
03302     nscoord extraSpace = mRect.width - trueDimensions.width;
03303 
03304     if (extraSpace > 0 && aNumJustifiableCharacter > 0) {
03305       aTextStyle.mExtraSpacePerJustifiableCharacter = extraSpace/aNumJustifiableCharacter;
03306       aTextStyle.mNumJustifiableCharacterReceivingExtraJot =
03307         extraSpace - aTextStyle.mExtraSpacePerJustifiableCharacter*aNumJustifiableCharacter;
03308     }
03309   }
03310 }
03311 
03312 void
03313 nsTextFrame::PaintTextSlowly(nsPresContext* aPresContext,
03314                              nsIRenderingContext& aRenderingContext,
03315                              nsStyleContext* aStyleContext,
03316                              TextPaintStyle& aTextStyle,
03317                              nscoord dx, nscoord dy)
03318 {
03319   nsCOMPtr<nsISelectionController> selCon;
03320   nsCOMPtr<nsIPresShell> shell;
03321   PRBool  displaySelection;
03322   PRBool  isPaginated,canDarkenColor=PR_FALSE;
03323   PRBool  isSelected;
03324   PRBool  hideStandardSelection;
03325   PRInt16 selectionValue;
03326   nsCOMPtr<nsILineBreaker> lb;
03327   if (NS_FAILED(GetTextInfoForPainting(aPresContext, 
03328                                        aRenderingContext,
03329                                        getter_AddRefs(shell),
03330                                        getter_AddRefs(selCon),
03331                                        displaySelection,
03332                                        isPaginated,
03333                                        isSelected,
03334                                        hideStandardSelection,
03335                                        selectionValue,
03336                                        getter_AddRefs(lb)))) {
03337      return;
03338   }
03339 
03340 
03341   if(isPaginated){
03342     canDarkenColor = CanDarken(aPresContext);
03343   }
03344 
03345   // Make enough space to transform
03346   nsAutoTextBuffer paintBuffer;
03347   nsAutoIndexBuffer indexBuffer;
03348   if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) {
03349     return;
03350   }
03351   nscoord width = mRect.width;
03352   PRInt32 textLength;
03353 
03354   nsTextTransformer tx(lb, nsnull, aPresContext);
03355   PRIntn numJustifiableCharacter;
03356   
03357   PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull),
03358                      &paintBuffer, &textLength, PR_TRUE, &numJustifiableCharacter);
03359 
03360   PRInt32* ip = indexBuffer.mBuffer;
03361   PRUnichar* text = paintBuffer.mBuffer;
03362 
03363   if (0 != textLength) {
03364 #ifdef IBMBIDI
03365     PRBool isRightToLeftOnBidiPlatform = PR_FALSE;
03366     PRBool isBidiSystem = PR_FALSE;
03367     PRBool isOddLevel = PR_FALSE;
03368     PRUint32 hints = 0;
03369     aRenderingContext.GetHints(hints);
03370     PRBool paintCharByChar = (0 == (hints & NS_RENDERING_HINT_REORDER_SPACED_TEXT)) &&
03371       ((0 != aTextStyle.mLetterSpacing) ||
03372        (0 != aTextStyle.mWordSpacing) ||
03373        aTextStyle.mJustifying);
03374     nsCharType charType = eCharType_LeftToRight;
03375 
03376     if (aPresContext->BidiEnabled()) {
03377       isBidiSystem = aPresContext->IsBidiSystem();
03378       nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils();
03379 
03380       if (bidiUtils) {
03381         isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
03382         charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsLayoutAtoms::charType));
03383 #ifdef DEBUG
03384         PRInt32 rememberTextLength = textLength;
03385 #endif
03386         isRightToLeftOnBidiPlatform = (!paintCharByChar &&
03387                                        isBidiSystem &&
03388                                        (eCharType_RightToLeft == charType ||
03389                                         eCharType_RightToLeftArabic == charType));
03390         if (isRightToLeftOnBidiPlatform) {
03391           // indicate that the platform should use its native
03392           // capabilities to reorder the text with right-to-left
03393           // base direction 
03394           aRenderingContext.SetRightToLeftText(PR_TRUE);
03395         }
03396         // If we will be painting char by char, handle the text like on non-bidi platform
03397         bidiUtils->ReorderUnicodeText(text, textLength, charType,
03398                                       isOddLevel, (paintCharByChar) ? PR_FALSE : isBidiSystem);
03399         NS_ASSERTION(rememberTextLength == textLength, "Bidi formatting changed text length");
03400       }
03401     }
03402 #endif // IBMBIDI
03403     ComputeExtraJustificationSpacing(aRenderingContext, aTextStyle, text, textLength, numJustifiableCharacter);
03404     if (!displaySelection || !isSelected) { 
03405       // When there is no selection showing, use the fastest and
03406       // simplest rendering approach
03407       aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
03408       RenderString(aRenderingContext, aStyleContext, aPresContext, aTextStyle,
03409                    PR_FALSE, text, textLength, PR_TRUE, dx, dy, width);
03410     }
03411     else 
03412     {
03413       SelectionDetails *details = nsnull;
03414       nsCOMPtr<nsIFrameSelection> frameSelection;
03415 //get the frame selection
03416       nsresult rv = NS_OK;
03417       frameSelection = do_QueryInterface(selCon); //this MAY implement
03418       if (!frameSelection)//if that failed get it from the presshell
03419         frameSelection = shell->FrameSelection();
03420       nsCOMPtr<nsIContent> content;
03421       PRInt32 offset;
03422       PRInt32 length;
03423 
03424       rv = GetContentAndOffsetsForSelection(aPresContext,
03425                                             getter_AddRefs(content),
03426                                             &offset, &length);
03427       if (NS_SUCCEEDED(rv)) {
03428         rv = frameSelection->LookUpSelection(content, mContentOffset, 
03429                                              mContentLength, &details,
03430                                              PR_FALSE);
03431       }
03432 
03433       //where are the selection points "really"
03434       SelectionDetails *sdptr = details;
03435       while (sdptr){
03436         sdptr->mStart = ip[sdptr->mStart] - mContentOffset;
03437         sdptr->mEnd = ip[sdptr->mEnd]  - mContentOffset;
03438 #ifdef IBMBIDI
03439         AdjustSelectionPointsForBidi(sdptr, textLength,
03440                                      CHARTYPE_IS_RTL(charType), isOddLevel,
03441                                      (paintCharByChar) ? PR_FALSE : isBidiSystem);
03442 #endif
03443         sdptr = sdptr->mNext;
03444       }
03445 
03446       DrawSelectionIterator iter(content, details,text,(PRUint32)textLength,aTextStyle, selectionValue, aPresContext, mStyleContext);
03447       if (!iter.IsDone() && iter.First())
03448       {
03449         nscoord currentX = dx;
03450         nsTextDimensions newDimensions;//temp
03451 #ifdef IBMBIDI // Simon - display substrings RTL in RTL frame
03452         if (isRightToLeftOnBidiPlatform)
03453         {
03454           nsTextDimensions frameDimensions;
03455           GetTextDimensions(aRenderingContext, aTextStyle, text, 
03456                             (PRInt32)textLength, iter.IsLast(), &frameDimensions);
03457           currentX = dx + frameDimensions.width;
03458         }
03459 #endif
03460         while (!iter.IsDone())
03461         {
03462           PRUnichar *currenttext  = iter.CurrentTextUnicharPtr();
03463           PRUint32   currentlength= iter.CurrentLength();
03464           //TextStyle &currentStyle = iter.CurrentStyle();
03465           nscolor    currentFGColor, currentBKColor;
03466           PRBool     isCurrentBKColorTransparent;
03467           PRBool     isSelection = iter.GetSelectionColors(&currentFGColor,
03468                                                            &currentBKColor,
03469                                                            &isCurrentBKColorTransparent);
03470           PRBool     isEndOfFrame = iter.IsLast();
03471           GetTextDimensions(aRenderingContext, aTextStyle, currenttext,
03472                             (PRInt32)currentlength, isEndOfFrame, &newDimensions);
03473           if (newDimensions.width)
03474           {
03475 #ifdef IBMBIDI
03476             if (isRightToLeftOnBidiPlatform)
03477               currentX -= newDimensions.width;
03478 #endif
03479             if (isSelection && !isPaginated)
03480             {//DRAW RECT HERE!!!
03481               if (!isCurrentBKColorTransparent) {
03482                 aRenderingContext.SetColor(currentBKColor);
03483                 aRenderingContext.FillRect(currentX, dy, newDimensions.width, mRect.height);
03484               }
03485             }
03486           }
03487 
03488           if (isPaginated && !iter.IsBeforeOrAfter()) {
03489             aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor, canDarkenColor));
03490             RenderString(aRenderingContext, aStyleContext, aPresContext,
03491                          aTextStyle, isRightToLeftOnBidiPlatform, 
03492                          currenttext, currentlength, isEndOfFrame,
03493                          currentX, dy, newDimensions.width, details);
03494           } else if (!isPaginated) {
03495             aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor, canDarkenColor));
03496             RenderString(aRenderingContext,aStyleContext, aPresContext,
03497                          aTextStyle, isRightToLeftOnBidiPlatform, 
03498                          currenttext, currentlength, isEndOfFrame,
03499                          currentX, dy, newDimensions.width, details);
03500           }
03501 
03502 #ifdef IBMBIDI
03503           if (!isRightToLeftOnBidiPlatform)
03504 #endif
03505           // increment twips X start but remember to get ready for
03506           // next draw by reducing current x by letter spacing amount
03507           currentX += newDimensions.width; // + aTextStyle.mLetterSpacing;
03508 
03509           iter.Next();
03510         }
03511       }
03512       else if (!isPaginated) 
03513       {
03514         aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
03515         RenderString(aRenderingContext, aStyleContext, aPresContext,
03516                      aTextStyle, isRightToLeftOnBidiPlatform, text, 
03517                      PRUint32(textLength), PR_TRUE, dx, dy, width, details);
03518       }
03519       sdptr = details;
03520       if (details){
03521         while ((sdptr = details->mNext) != nsnull) {
03522           delete details;
03523           details = sdptr;
03524         }
03525         delete details;
03526       }
03527     }
03528 #ifdef IBMBIDI
03529     if (isRightToLeftOnBidiPlatform) {
03530       // indicate that future text should not be reordered with
03531       // right-to-left base direction 
03532       aRenderingContext.SetRightToLeftText(PR_FALSE);
03533     }
03534 #endif // IBMBIDI
03535   }
03536 }
03537 
03538 void
03539 nsTextFrame::PaintAsciiText(nsPresContext* aPresContext,
03540                             nsIRenderingContext& aRenderingContext,
03541                             nsStyleContext* aStyleContext,
03542                             TextPaintStyle& aTextStyle,
03543                             nscoord dx, nscoord dy)
03544 {
03545   NS_PRECONDITION(0 == (TEXT_HAS_MULTIBYTE & mState), "text is multi-byte");
03546 
03547   nsCOMPtr<nsISelectionController> selCon;
03548   nsCOMPtr<nsIPresShell> shell;
03549   PRBool  displaySelection,canDarkenColor=PR_FALSE;
03550   PRBool  isPaginated;
03551   PRBool  isSelected;
03552   PRBool  hideStandardSelection;
03553   PRInt16 selectionValue;
03554   nsCOMPtr<nsILineBreaker> lb;
03555   if (NS_FAILED(GetTextInfoForPainting(aPresContext, 
03556                                        aRenderingContext,
03557                                        getter_AddRefs(shell),
03558                                        getter_AddRefs(selCon),
03559                                        displaySelection,
03560                                        isPaginated,
03561                                        isSelected,
03562                                        hideStandardSelection,
03563                                        selectionValue,
03564                                        getter_AddRefs(lb)))) {
03565      return;
03566   }
03567 
03568   if(isPaginated){
03569     canDarkenColor = CanDarken(aPresContext);
03570   }
03571 
03572   // Get the text fragment
03573   nsCOMPtr<nsITextContent> tc = do_QueryInterface(mContent);
03574   const nsTextFragment* frag = nsnull;
03575   if (tc) {
03576     frag = tc->Text();
03577 
03578     if (!frag) {
03579       return;
03580     }
03581   }
03582 
03583   // Make enough space to transform
03584   nsAutoTextBuffer unicodePaintBuffer;
03585   nsAutoIndexBuffer indexBuffer;
03586   if (displaySelection) {
03587     if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) {
03588       return;
03589     }
03590   }
03591 
03592   nsTextTransformer tx(lb, nsnull, aPresContext);
03593 
03594   // See if we need to transform the text. If the text fragment is ascii and
03595   // wasn't transformed, then we can skip this step. If we're displaying the
03596   // selection and the text is selected, then we need to do this step so we
03597   // can create the index buffer
03598   PRInt32     textLength = 0;
03599   const char* text;
03600   char        paintBufMem[TEXT_BUF_SIZE];
03601   char*       paintBuf = paintBufMem;
03602   if (frag->Is2b() ||
03603       (0 != (mState & TEXT_WAS_TRANSFORMED)) ||
03604       (displaySelection && isSelected)) {
03605     
03606     // Transform text from content into Unicode renderable form
03607     // XXX If the text fragment is ascii, then we should ask the
03608     // text transformer to leave the text in ascii. That way we can
03609     // elimninate the conversion from Unicode back to ascii...
03610     PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull),
03611                        &unicodePaintBuffer, &textLength);
03612 
03613 
03614     // Translate unicode data into ascii for rendering
03615     if (textLength > TEXT_BUF_SIZE) {
03616       paintBuf = new char[textLength];
03617       if (!paintBuf) {
03618         return;
03619       }
03620     }
03621     char* dst = paintBuf;
03622     char* end = dst + textLength;
03623     PRUnichar* src = unicodePaintBuffer.mBuffer;
03624     while (dst < end) {
03625       *dst++ = (char) ((unsigned char) *src++);
03626     }
03627 
03628     text = paintBuf;
03629 
03630   }
03631   else if (mContentOffset + mContentLength <= frag->GetLength()) {
03632     text = frag->Get1b() + mContentOffset;
03633     textLength = mContentLength;
03634 
03635     // See if we should skip leading whitespace
03636     if (0 != (mState & TEXT_SKIP_LEADING_WS)) {
03637       while ((textLength > 0) && XP_IS_SPACE(*text)) {
03638         text++;
03639         textLength--;
03640       }
03641     }
03642 
03643     // See if the text ends in a newline
03644     if ((textLength > 0) && (text[textLength - 1] == '\n')) {
03645       textLength--;
03646     }
03647     NS_ASSERTION(textLength >= 0, "bad text length");
03648   }
03649   else {
03650     // This might happen if a paint event beats the reflow; e.g., as
03651     // is the case in bug 73291. Not a big deal, because the reflow
03652     // will schedule another invalidate.
03653     NS_WARNING("content length exceeds fragment length");
03654   }
03655 
03656   nscoord width = mRect.width;
03657   PRInt32* ip = indexBuffer.mBuffer;
03658 
03659   if (0 != textLength) {
03660     if (!displaySelection || !isSelected) { 
03661       //if selection is > content length then selection has "slid off"
03662       // When there is no selection showing, use the fastest and
03663       // simplest rendering approach
03664       aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
03665       aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
03666       PaintTextDecorations(aRenderingContext, aStyleContext,
03667                            aPresContext, aTextStyle, dx, dy, width, PR_FALSE);
03668     }
03669     else {
03670       SelectionDetails *details;
03671       nsCOMPtr<nsIFrameSelection> frameSelection;
03672 //get the frame selection
03673       frameSelection = do_QueryInterface(selCon); //this MAY implement
03674       nsresult rv = NS_OK;
03675       if (!frameSelection)//if that failed get it from the presshell
03676         frameSelection = shell->FrameSelection();
03677       nsCOMPtr<nsIContent> content;
03678       PRInt32 offset;
03679       PRInt32 length;
03680 
03681       rv = GetContentAndOffsetsForSelection(aPresContext,
03682                                             getter_AddRefs(content),
03683                                             &offset, &length);
03684       if (NS_SUCCEEDED(rv)) {
03685         rv = frameSelection->LookUpSelection(content, mContentOffset, 
03686                                              mContentLength, &details,
03687                                              PR_FALSE);
03688       }
03689         
03690       //where are the selection points "really"
03691       SelectionDetails *sdptr = details;
03692       while (sdptr){
03693         sdptr->mStart = ip[sdptr->mStart] - mContentOffset;
03694         sdptr->mEnd = ip[sdptr->mEnd]  - mContentOffset;
03695         sdptr = sdptr->mNext;
03696       }
03697 
03698       if (!hideStandardSelection || displaySelection) {
03699         //ITS OK TO CAST HERE THE RESULT WE USE WILLNOT DO BAD CONVERSION
03700         DrawSelectionIterator iter(content, details,(PRUnichar *)text,(PRUint32)textLength,aTextStyle,
03701                                    selectionValue, aPresContext, mStyleContext);
03702 
03703         // See if this rendering backend supports getting cluster
03704         // information.
03705         PRUint32 clusterHint = 0;
03706         aRenderingContext.GetHints(clusterHint);
03707         clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
03708 
03709         nscoord foo;
03710         aRenderingContext.GetWidth(text, textLength, foo);
03711 
03712         if (!iter.IsDone() && iter.First())
03713         {
03714           nscoord currentX = dx;
03715           nscoord newWidth;//temp
03716           while (!iter.IsDone())
03717           {
03718             char *currenttext  = iter.CurrentTextCStrPtr();
03719             PRUint32   currentlength= iter.CurrentLength();
03720             //TextStyle &currentStyle = iter.CurrentStyle();
03721             nscolor    currentFGColor, currentBKColor;
03722             PRBool     isCurrentBKColorTransparent;
03723 
03724             if (clusterHint) {
03725               PRUint32 tmpWidth;
03726               rv = aRenderingContext.GetRangeWidth(text, textLength, currenttext - text,
03727                                                    (currenttext - text) + currentlength,
03728                                                    tmpWidth);
03729               newWidth = nscoord(tmpWidth);
03730             }
03731             else {
03732               rv = aRenderingContext.GetWidth(currenttext, currentlength,newWidth); //ADJUST FOR CHAR SPACING
03733             }
03734 
03735             PRBool     isSelection = iter.GetSelectionColors(&currentFGColor,
03736                                                              &currentBKColor,
03737                                                              &isCurrentBKColorTransparent);
03738 
03739             if (NS_SUCCEEDED(rv)) {
03740               if (isSelection && !isPaginated)
03741               {//DRAW RECT HERE!!!
03742                 if (!isCurrentBKColorTransparent) {
03743                   aRenderingContext.SetColor(currentBKColor);
03744                   aRenderingContext.FillRect(currentX, dy, newWidth, mRect.height);
03745                 }
03746               }
03747             }
03748             else {
03749               newWidth =0;
03750             }
03751 
03752             aRenderingContext.PushState();
03753 
03754             nsRect rect(currentX, dy, newWidth, mRect.height);
03755             aRenderingContext.SetClipRect(rect, nsClipCombine_kIntersect);
03756 
03757             if (isPaginated && !iter.IsBeforeOrAfter()) {
03758               aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
03759               aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
03760             } else if (!isPaginated) {
03761               aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor,canDarkenColor));
03762               aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
03763             }
03764 
03765             aRenderingContext.PopState();
03766 
03767             currentX+=newWidth;//increment twips X start
03768 
03769             iter.Next();
03770           }
03771         }
03772         else if (!isPaginated) 
03773         {
03774           aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
03775           aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
03776         }
03777       }
03778 
03779       PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
03780                            aTextStyle, dx, dy, width, PR_FALSE,
03781                            unicodePaintBuffer.mBuffer,
03782                            details, 0, textLength);
03783       sdptr = details;
03784       if (details){
03785         while ((sdptr = details->mNext) != nsnull) {
03786           delete details;
03787           details = sdptr;
03788         }
03789         delete details;
03790       }
03791     }
03792   }
03793 
03794   // Cleanup
03795   if (paintBuf != paintBufMem) {
03796     delete [] paintBuf;
03797   }
03798 }
03799 
03800 //---------------------------------------------------
03801 // Also defined for external use in nsTextFrame.h
03802 //
03803 // Uses a binary search for find where the cursor falls in the line of text
03804 // It also keeps track of the part of the string that has already been measured
03805 // so it doesn't have to keep measuring the same text over and over
03806 //
03807 // Param "aBaseWidth" contains the width in twips of the portion 
03808 // of the text that has already been measured, and aBaseInx contains
03809 // the index of the text that has already been measured.
03810 //
03811 // aTextWidth returns the (in twips) the length of the text that falls before the cursor
03812 // aIndex contains the index of the text where the cursor falls
03813 PRBool
03814 BinarySearchForPosition(nsIRenderingContext* aRendContext, 
03815                         const PRUnichar* aText,
03816                         PRInt32    aBaseWidth,
03817                         PRInt32    aBaseInx,
03818                         PRInt32    aStartInx, 
03819                         PRInt32    aEndInx, 
03820                         PRInt32    aCursorPos, 
03821                         PRInt32&   aIndex,
03822                         PRInt32&   aTextWidth)
03823 {
03824   PRInt32 range = aEndInx - aStartInx;
03825   if ((range == 1) || (range == 2 && IS_HIGH_SURROGATE(aText[aStartInx]))) {
03826     aIndex   = aStartInx + aBaseInx;
03827     aRendContext->GetWidth(aText, aIndex, aTextWidth);
03828     return PR_TRUE;
03829   }
03830 
03831   PRInt32 inx = aStartInx + (range / 2);
03832 
03833   // Make sure we don't leave a dangling low surrogate
03834   if (IS_HIGH_SURROGATE(aText[inx-1]))
03835     inx++;
03836 
03837   PRInt32 textWidth = 0;
03838   aRendContext->GetWidth(aText, inx, textWidth);
03839 
03840   PRInt32 fullWidth = aBaseWidth + textWidth;
03841   if (fullWidth == aCursorPos) {
03842     aTextWidth = textWidth;
03843     aIndex = inx;
03844     return PR_TRUE;
03845   } else if (aCursorPos < fullWidth) {
03846     aTextWidth = aBaseWidth;
03847     if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, aStartInx, inx, aCursorPos, aIndex, aTextWidth)) {
03848       return PR_TRUE;
03849     }
03850   } else {
03851     aTextWidth = fullWidth;
03852     if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, inx, aEndInx, aCursorPos, aIndex, aTextWidth)) {
03853       return PR_TRUE;
03854     }
03855   }
03856   return PR_FALSE;
03857 }
03858 
03859 //---------------------------------------------------------------------------
03860 // Uses a binary search to find the position of the cursor in the text.
03861 // The "indices array is used to map from the compressed text back to the 
03862 // un-compressed text, selection is based on the un-compressed text, the visual 
03863 // display of selection is based on the compressed text.
03864 //---------------------------------------------------------------------------
03865 NS_IMETHODIMP
03866 nsTextFrame::GetPosition(nsPresContext*  aPresContext,
03867                          const nsPoint&  aPoint,
03868                          nsIContent **   aNewContent,
03869                          PRInt32&        aContentOffset,
03870                          PRInt32&        aContentOffsetEnd)
03871 
03872 {
03873   // pre-condition tests
03874   NS_PRECONDITION(aPresContext && aNewContent, "null arg");
03875   if (!aPresContext || !aNewContent) {
03876     return NS_ERROR_NULL_POINTER;
03877   }
03878   // initialize out param
03879   *aNewContent = nsnull;
03880 
03881   DEBUG_VERIFY_NOT_DIRTY(mState);
03882   if (mState & NS_FRAME_IS_DIRTY)
03883     return NS_ERROR_UNEXPECTED;
03884 
03885   nsIPresShell *shell = aPresContext->GetPresShell();
03886   if (shell) {
03887     nsCOMPtr<nsIRenderingContext> rendContext;      
03888     nsresult rv = shell->CreateRenderingContext(this, getter_AddRefs(rendContext));
03889     if (NS_SUCCEEDED(rv)) {
03890       TextStyle ts(aPresContext, *rendContext, mStyleContext);
03891       if (ts.mSmallCaps || ts.mWordSpacing || ts.mLetterSpacing || ts.mJustifying) {
03892         nsresult result = GetPositionSlowly(aPresContext, rendContext, aPoint, aNewContent,
03893                                  aContentOffset);
03894         aContentOffsetEnd = aContentOffset;
03895         return result;
03896       }
03897 
03898       // Make enough space to transform
03899       nsAutoTextBuffer paintBuffer;
03900       nsAutoIndexBuffer indexBuffer;
03901       rv = indexBuffer.GrowTo(mContentLength + 1);
03902       if (NS_FAILED(rv)) {
03903         return rv;
03904       }
03905 
03906       // Find the font metrics for this text
03907       SetFontFromStyle(rendContext, mStyleContext);
03908 
03909       // Get the renderable form of the text
03910       nsIDocument *doc = GetDocument(aPresContext);
03911       nsTextTransformer tx(doc->GetLineBreaker(), nsnull, aPresContext);
03912       PRInt32 textLength;
03913       // no need to worry about justification, that's always on the slow path
03914       PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
03915 
03916       nsPoint origin;
03917       nsIView * view;
03918       GetOffsetFromView(origin, &view);
03919 
03920 //IF STYLE SAYS TO SELECT TO END OF FRAME HERE...
03921       PRInt32 prefInt =
03922         nsContentUtils::GetIntPref("browser.drag_out_of_frame_style");
03923       PRBool outofstylehandled = PR_FALSE;
03924 
03925       if (prefInt)
03926       {
03927         if ((aPoint.y - origin.y) < 0)//above rectangle
03928         {
03929           aContentOffset = mContentOffset;
03930           aContentOffsetEnd = aContentOffset;
03931           outofstylehandled = PR_TRUE;
03932         }
03933         else if ((aPoint.y - origin.y) > mRect.height)
03934         {
03935           aContentOffset = mContentOffset + mContentLength;
03936           aContentOffsetEnd = aContentOffset;
03937           outofstylehandled = PR_TRUE;
03938         }
03939       }
03940 
03941       if (textLength <= 0) {
03942         aContentOffset = mContentOffset;
03943         aContentOffsetEnd = aContentOffset;
03944       }
03945       else if (!outofstylehandled) //then we need to track based on the X coord only
03946       {
03947 //END STYLE IF
03948         PRInt32* ip = indexBuffer.mBuffer;
03949 
03950         PRInt32 indx;
03951         PRInt32 textWidth = 0;
03952         PRUnichar* text = paintBuffer.mBuffer;
03953 
03954         // See if the font backend will do all the hard work for us.
03955         PRUint32 clusterHint = 0;
03956         rendContext->GetHints(clusterHint);
03957         clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
03958         if (clusterHint) {
03959           nsPoint pt;
03960           pt.x = aPoint.x - origin.x;
03961           pt.y = aPoint.y - origin.y;
03962           indx = rendContext->GetPosition(text, textLength, pt);
03963         }
03964         else {
03965 #ifdef IBMBIDI
03966         PRBool getReversedPos = NS_GET_EMBEDDING_LEVEL(this) & 1;
03967         nscoord posX = (getReversedPos) ?
03968                        (mRect.width + origin.x) - (aPoint.x - origin.x) : aPoint.x;
03969 
03970         PRBool found = BinarySearchForPosition(rendContext, text, origin.x, 0, 0,
03971                                                PRInt32(textLength),
03972                                                PRInt32(posX) , //go to local coordinates
03973                                                indx, textWidth);
03974 
03975 #else
03976         PRBool found = BinarySearchForPosition(rendContext, text, origin.x, 0, 0,
03977                                                PRInt32(textLength),
03978                                                PRInt32(aPoint.x) , //go to local coordinates
03979                                                indx, textWidth);
03980 #endif // IBMBIDI
03981         if (found) {
03982           PRInt32 charWidth;
03983           if (IS_HIGH_SURROGATE(text[indx]))
03984             rendContext->GetWidth(&text[indx], 2, charWidth);
03985           else
03986             rendContext->GetWidth(text[indx], charWidth);
03987           charWidth /= 2;
03988 
03989 #ifdef IBMBIDI
03990           if (getReversedPos) {
03991             if (mRect.width - aPoint.x + origin.x > textWidth+charWidth ) {
03992               indx++;
03993             }
03994           }
03995           else
03996 #endif // IBMBIDI
03997           if ((aPoint.x - origin.x) > textWidth+charWidth) {
03998             indx++;
03999           }
04000         }
04001         }
04002 
04003         aContentOffset = indx + mContentOffset;
04004         //reusing wordBufMem
04005         PRInt32 i;
04006         for (i = 0; i < mContentLength; i ++){
04007           if ((ip[i] >= aContentOffset) && //reverse mapping
04008               (! IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) {
04009               break;
04010           }
04011         }
04012         aContentOffset = i + mContentOffset;
04013 #ifdef IBMBIDI
04014         PRInt32 bidiStopOffset = mContentOffset + mContentLength;
04015 
04016         if (aContentOffset >= mContentOffset && aContentOffset < bidiStopOffset) {
04017           PRInt32 curindx = ip[aContentOffset - mContentOffset] - mContentOffset;
04018           while (curindx < textLength && IS_BIDI_DIACRITIC(text[curindx])) {
04019             if (++aContentOffset >= bidiStopOffset)
04020               break;
04021             curindx = ip[aContentOffset - mContentOffset] - mContentOffset;
04022           }
04023         }
04024 #endif // IBMBIDI
04025         aContentOffsetEnd = aContentOffset;
04026         NS_ASSERTION(i<= mContentLength, "offset we got from binary search is messed up");
04027       }      
04028       *aNewContent = mContent;
04029       if (*aNewContent) {
04030         (*aNewContent)->AddRef();
04031       }
04032     }
04033   }
04034   return NS_OK;
04035 }
04036 
04037 NS_IMETHODIMP
04038 nsTextFrame::GetContentAndOffsetsFromPoint(nsPresContext*  aPresContext,
04039                                            const nsPoint&  aPoint,
04040                                            nsIContent **   aNewContent,
04041                                            PRInt32&        aContentOffset,
04042                                            PRInt32&        aContentOffsetEnd,
04043                                            PRBool&         aBeginFrameContent)
04044 {
04045   if (!aNewContent)
04046     return NS_ERROR_NULL_POINTER;
04047   *aNewContent = nsnull;//initialize
04048   aContentOffset = 0;
04049   aContentOffsetEnd = 0;
04050   aBeginFrameContent = 0;
04051 
04052   DEBUG_VERIFY_NOT_DIRTY(mState);
04053   if (mState & NS_FRAME_IS_DIRTY)
04054     return NS_ERROR_UNEXPECTED;
04055 
04056   nsPoint newPoint;
04057   newPoint.y = aPoint.y;
04058   if (aPoint.x < 0)
04059     newPoint.x = 0;
04060   else
04061     newPoint.x = aPoint.x;
04062   nsresult rv = GetPosition(aPresContext, newPoint, aNewContent, aContentOffset, aContentOffsetEnd);
04063   if (NS_FAILED(rv))
04064     return rv;
04065   if (aContentOffset == mContentOffset)
04066     aBeginFrameContent = PR_TRUE;
04067   else
04068     aBeginFrameContent = PR_FALSE;
04069   return rv;
04070 }
04071 
04072 
04073 // [HACK] Foward Declarations
04074 void ForceDrawFrame(nsFrame * aFrame);
04075 
04076 //null range means the whole thing
04077 NS_IMETHODIMP
04078 nsTextFrame::SetSelected(nsPresContext* aPresContext,
04079                          nsIDOMRange *aRange,
04080                          PRBool aSelected,
04081                          nsSpread aSpread)
04082 {
04083   if (aSelected && ParentDisablesSelection())
04084     return NS_OK;
04085 
04086 #if 0
04087   PRBool isSelected = ((GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT);
04088   if (!aSelected && !isSelected) //already set thanks
04089   {
04090     return NS_OK;
04091   }
04092 #endif
04093 
04094   // check whether style allows selection
04095   PRBool selectable;
04096   IsSelectable(&selectable, nsnull);
04097   if (!selectable)
04098     return NS_OK;//do not continue no selection for this frame.
04099 
04100   PRBool found = PR_FALSE;
04101   if (aRange) {
04102     //lets see if the range contains us, if so we must redraw!
04103     nsCOMPtr<nsIDOMNode> endNode;
04104     PRInt32 endOffset;
04105     nsCOMPtr<nsIDOMNode> startNode;
04106     PRInt32 startOffset;
04107     aRange->GetEndContainer(getter_AddRefs(endNode));
04108     aRange->GetEndOffset(&endOffset);
04109     aRange->GetStartContainer(getter_AddRefs(startNode));
04110     aRange->GetStartOffset(&startOffset);
04111     nsCOMPtr<nsIDOMNode> thisNode = do_QueryInterface(GetContent());
04112 
04113     if (thisNode == startNode)
04114     {
04115       if ((mContentOffset + mContentLength) >= startOffset)
04116       {
04117         found = PR_TRUE;
04118         if (thisNode == endNode)
04119         { //special case
04120           if (endOffset == startOffset) //no need to redraw since drawing takes place with cursor
04121             found = PR_FALSE;
04122 
04123           if (mContentOffset > endOffset)
04124             found = PR_FALSE;
04125         }
04126       }
04127     }
04128     else if (thisNode == endNode)
04129     {
04130       if (mContentOffset < endOffset)
04131         found = PR_TRUE;
04132       else
04133       {
04134         found = PR_FALSE;
04135       }
04136     }
04137     else
04138     {
04139       found = PR_TRUE;
04140     }
04141   }
04142   else {
04143     // null range means the whole thing
04144     found = PR_TRUE;
04145   }
04146 
04147   if ( aSelected )
04148     AddStateBits(NS_FRAME_SELECTED_CONTENT);
04149   else
04150   {//we need to see if any other selection available.
04151     SelectionDetails *details = nsnull;
04152     nsCOMPtr<nsIFrameSelection> frameSelection;
04153 
04154     nsIPresShell *shell = aPresContext->GetPresShell();
04155     if (shell) {
04156       nsCOMPtr<nsISelectionController> selCon;
04157       nsresult rv = GetSelectionController(aPresContext,
04158                                            getter_AddRefs(selCon));
04159       if (NS_SUCCEEDED(rv) && selCon)
04160       {
04161         frameSelection = do_QueryInterface(selCon); //this MAY implement
04162       }
04163       if (!frameSelection)
04164         frameSelection = shell->FrameSelection();
04165 
04166       nsCOMPtr<nsIContent> content;
04167       PRInt32 offset;
04168       PRInt32 length;
04169 
04170       rv = GetContentAndOffsetsForSelection(aPresContext,
04171                                             getter_AddRefs(content),
04172                                             &offset, &length);
04173       if (NS_SUCCEEDED(rv) && content) {
04174         rv = frameSelection->LookUpSelection(content, offset,
04175                                              length, &details, PR_TRUE);
04176         // PR_TRUE last param used here! we need to see if we are still selected. so no shortcut
04177       }
04178     }
04179     if (!details)
04180       RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
04181     else
04182     {
04183       SelectionDetails *sdptr = details;
04184       while ((sdptr = details->mNext) != nsnull) {
04185         delete details;
04186         details = sdptr;
04187       }
04188       delete details;
04189     }
04190   }
04191   if (found){ //if range contains this frame...
04192     // Selection might change our border, content and outline appearance
04193     // But textframes can't have an outline. So just use the simple
04194     // bounds
04195     Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_FALSE);
04196   }
04197   if (aSpread == eSpreadDown)
04198   {
04199     nsIFrame* frame = GetPrevInFlow();
04200     while(frame){
04201       frame->SetSelected(aPresContext, aRange,aSelected,eSpreadNone);
04202       frame = frame->GetPrevInFlow();
04203     }
04204     frame = GetNextInFlow();
04205     while (frame){
04206       frame->SetSelected(aPresContext, aRange,aSelected,eSpreadNone);
04207       frame = frame->GetNextInFlow();
04208     }
04209 #ifdef IBMBIDI
04210     if ((mState & NS_FRAME_IS_BIDI) &&
04211         (frame = NS_STATIC_CAST(nsIFrame*,
04212                                 aPresContext->PropertyTable()->GetProperty(this, nsLayoutAtoms::nextBidi)))) {
04213       frame->SetSelected(aPresContext, aRange, aSelected, aSpread);
04214     }
04215 #endif // IBMBIDI
04216   }
04217   return NS_OK;
04218 }
04219 
04220 NS_IMETHODIMP
04221 nsTextFrame::GetPointFromOffset(nsPresContext* aPresContext,
04222                                 nsIRenderingContext* inRendContext,
04223                                 PRInt32 inOffset,
04224                                 nsPoint* outPoint)
04225 {
04226   if (!aPresContext || !inRendContext || !outPoint)
04227     return NS_ERROR_NULL_POINTER;
04228 
04229   outPoint->x = 0;
04230   outPoint->y = 0;
04231 
04232   DEBUG_VERIFY_NOT_DIRTY(mState);
04233   if (mState & NS_FRAME_IS_DIRTY)
04234     return NS_ERROR_UNEXPECTED;
04235 
04236   if (mContentLength <= 0) {
04237     return NS_OK;
04238   }
04239 
04240   inOffset-=mContentOffset;
04241   if (inOffset < 0){
04242     NS_ASSERTION(0,"offset less than this frame has in GetPointFromOffset");
04243     inOffset = 0;
04244   }
04245   if (inOffset >= mContentLength)
04246     inOffset = mContentLength;
04247   TextStyle ts(aPresContext, *inRendContext, mStyleContext);
04248 
04249   // Make enough space to transform
04250   nsAutoTextBuffer paintBuffer;
04251   nsAutoIndexBuffer indexBuffer;
04252   nsresult rv = indexBuffer.GrowTo(mContentLength + 1);
04253   if (NS_FAILED(rv)) {
04254     return rv;
04255   }
04256 
04257   // Transform text from content into renderable form
04258   nsIDocument *doc = GetDocument(aPresContext);
04259   nsTextTransformer tx(doc->GetLineBreaker(), nsnull, aPresContext);
04260   PRInt32 textLength;
04261   PRIntn numJustifiableCharacter;
04262 
04263   PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength, PR_FALSE, &numJustifiableCharacter);
04264 
04265   ComputeExtraJustificationSpacing(*inRendContext, ts, paintBuffer.mBuffer, textLength, numJustifiableCharacter);
04266 
04267 
04268   PRInt32* ip = indexBuffer.mBuffer;
04269   if (inOffset > mContentLength){
04270     NS_ASSERTION(0, "invalid offset passed to GetPointFromOffset");
04271     inOffset = mContentLength;
04272   }
04273 
04274   while (inOffset >=0 && ip[inOffset] < mContentOffset) //buffer has shrunk
04275     inOffset --;
04276   nscoord width = mRect.width;
04277   if (inOffset <0)
04278   {
04279     NS_ASSERTION(0, "invalid offset passed to GetPointFromOffset");
04280     inOffset=0;
04281     width = 0;
04282   }
04283   else
04284   {
04285     PRInt32 hitLength = ip[inOffset] - mContentOffset;
04286     if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing) || ts.mJustifying)
04287     {
04288       nsTextDimensions dimensions;
04289       GetTextDimensions(*inRendContext, ts, paintBuffer.mBuffer, hitLength,
04290                         textLength == hitLength, &dimensions);
04291       width = dimensions.width;
04292     }
04293     else
04294     {
04295       PRInt32 totalLength = 0; // length up to the last-in-flow frame
04296       nsCOMPtr<nsITextContent> tc(do_QueryInterface(mContent));
04297       if (tc) {
04298         totalLength = tc->Text()->GetLength(); // raw value which includes whitespace
04299       }
04300       if ((hitLength == textLength) && (inOffset = mContentLength) &&
04301           (mContentOffset + mContentLength == totalLength)) {
04302         // no need to re-measure when at the end of the last-in-flow
04303       }
04304       else
04305         inRendContext->GetWidth(paintBuffer.mBuffer, hitLength, width);
04306     }
04307     if ((hitLength == textLength) && (TEXT_TRIMMED_WS & mState)) {
04308       //
04309       // Offset must be after a space that has
04310       // been trimmed off the end of the frame.
04311       // Add the width of the trimmed space back
04312       // to the total width, so the caret appears
04313       // in the proper place!
04314       //
04315       // NOTE: the trailing whitespace includes the word and letter spacing!!
04316       width += ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing;
04317     }
04318   }
04319 #ifdef IBMBIDI
04320   if (NS_GET_EMBEDDING_LEVEL(this) & 1) {
04321     if (width > mRect.width)
04322       outPoint->x = 0;
04323     else
04324       outPoint->x = mRect.width - width;
04325   }
04326   else
04327 #endif // IBMBIDI
04328   //XXX callers need to safeguard themselves against empty frames, I noted that
04329   //the caret can be locked when leftarrow'ing in: <span>...</span>\n<br>line
04330   if (width > mRect.width)
04331     outPoint->x = mRect.width;
04332   else
04333     outPoint->x = width;
04334   outPoint->y = 0;
04335 
04336   return NS_OK;
04337 }
04338 
04339 
04340 
04341 NS_IMETHODIMP
04342 nsTextFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset,
04343                                            PRBool  inHint,
04344                                            PRInt32* outFrameContentOffset,
04345                                            nsIFrame **outChildFrame)
04346 {
04347   if (nsnull == outChildFrame)
04348     return NS_ERROR_NULL_POINTER;
04349   PRInt32 contentOffset = inContentOffset;
04350   
04351   if (contentOffset != -1) //-1 signified the end of the current content
04352     contentOffset = inContentOffset - mContentOffset;
04353 
04354   if ((contentOffset > mContentLength) || ((contentOffset == mContentLength) && inHint) )
04355   {
04356     //this is not the frame we are looking for.
04357     nsIFrame* nextInFlow = GetNextInFlow();
04358     if (nextInFlow)
04359     {
04360       return nextInFlow->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
04361     }
04362     else {
04363 #ifdef IBMBIDI // Simon
04364     // There is no nextInFlow - check if there is a bidi
04365     // continuation frame
04366     if (mState & NS_FRAME_IS_BIDI)
04367     {
04368       nsIFrame *nextBidi = GetNextSibling();
04369       if (nextBidi)
04370       {
04371         PRInt32 start, end;
04372         if (NS_SUCCEEDED(nextBidi->GetOffsets(start, end)) && start > 0)
04373         {
04374           return nextBidi->GetChildFrameContainingOffset(inContentOffset, 
04375             inHint, outFrameContentOffset, outChildFrame);
04376         }
04377       }
04378     }
04379 #endif // IBMBIDI
04380       {
04381         if (contentOffset != mContentLength) //that condition was only for when there is a choice
04382           return NS_ERROR_FAILURE;
04383       }
04384     }
04385   }
04386 
04387   if (inContentOffset < mContentOffset) //could happen with floats!
04388   {
04389     *outChildFrame = GetPrevInFlow();
04390     if (*outChildFrame)
04391       return (*outChildFrame)->GetChildFrameContainingOffset(inContentOffset, inHint,
04392         outFrameContentOffset,outChildFrame);
04393     else
04394       return NS_OK; //this can't be the right thing to do?
04395   }
04396   
04397   *outFrameContentOffset = contentOffset;
04398   *outChildFrame = this;
04399   return NS_OK;
04400 }
04401 
04402 
04403 NS_IMETHODIMP
04404 nsTextFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos) 
04405 {
04406   DEBUG_VERIFY_NOT_DIRTY(mState);
04407   if (mState & NS_FRAME_IS_DIRTY)
04408     return NS_ERROR_UNEXPECTED;
04409 
04410 #ifdef IBMBIDI
04411   // XXX TODO: - this explanation may not be accurate
04412   //           - need to better explain what aPos->mPreferLeft means
04413   //             for two frames on the same line
04414   //           - need to better explain for what happens when you move
04415   //             from the end of a line to the beginning of the next 
04416   //             despite not making a move logically within the text.
04417   //           - need to explain why XORing with the embedding level
04418   //             achieves the desired effect
04419   //
04420   // When you move your caret by in some visual direction, and you find the
04421   // new position is at the edge of a line (beginning or end), you usually
04422   // want to stay on the current line rather than move to the next or the 
04423   // previous one; however, if you want to get to the edge of the next or
04424   // the previous word (i.e. you're 'eating white space' when moving
04425   // between words), and you couldn't find the word in the current line,
04426   // you do _not_ want to stay on the current line - you want to get to that
04427   // word.
04428   //
04429   // When your frames are ordered the same way logically as they are
04430   // visually (e.g. when they're frames of LTR text displayed in
04431   // left-then-down order), this translates to terms of 'prefer left' or
04432   // 'prefer right', i.e. if you move to the left and hit the beginning of
04433   // the line you have to choose between the frame "on the left" - the last
04434   // frame on the previous line - and the frame "on the right" - the
04435   // first frame of the current line.
04436   //
04437   // When the frames are displayed in right-then-down order (i.e. frames
04438   // within an RTL line), the last sentence remains correct, but now
04439   // the directions are reversed - if you're moving left, you hit the end
04440   // of a line; the frame closer to your original position is the one
04441   // "on the right" - the last frame on the current line - rather than the
04442   // one "on the left" - the first frame on the next
04443   // line.
04444   //
04445   // Now, it seems the PeekOffset() method does not _use_ the mPreferLeft
04446   // value, but it does _set_ it for the caller, and this must be done
04447   // consistently along calls to PeekOffset of nsTextFrames on the _same_
04448   // line as well as on different lines.
04449   //
04450   // Note:
04451   // eDirPrevious actually means 'in the visual left-then-down direction'
04452   // eDirNext actually means 'in the visual right-then-down direction'
04453   // (this is again due to the LTRish bias of the nomenclature)
04454 
04455   PRBool isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
04456 
04457   if ((eSelectCharacter == aPos->mAmount)
04458       || (eSelectWord == aPos->mAmount))
04459     // this 'flip' will be in effect only for this frame, not
04460     // for recursive calls to PeekOffset()
04461     aPos->mPreferLeft ^= isOddLevel;
04462 #endif
04463 
04464   if (!aPos || !mContent)
04465     return NS_ERROR_NULL_POINTER;
04466 
04467   // XXX TODO: explain this policy; why the assymetry between
04468   // too high and too low start offsets?
04469   //
04470   // There are 4 possible ranges of aPos->mStartOffset:
04471   //
04472   //            0    mContentOffset   mContentOffset+mContentLength
04473   //            |          |               |
04474   //   range #1 | range #2 |    range #3   | range #4
04475   //                        ***************
04476   //                       our frame's part
04477   //                        of the content
04478   //
04479   // Range   Policy
04480   // ------------------------------------------------------------------------
04481   //  #1     Assume the start position is at the end of the content of 'this'
04482   //  #2     Round up the position to the beginning of our frame
04483   //  #3     No change necessary
04484   //  #4     Delegate the PeekOffset() to the next frame (according
04485   //         to the direction, either to our left or our right)
04486   // 
04487   // Note: the diagram above is drawn left-to-right, but advancing
04488   // in the content may sometimes mean going right-to-left
04489   
04490   if (aPos->mStartOffset < 0 )
04491     aPos->mStartOffset = mContentLength + mContentOffset;
04492   if (aPos->mStartOffset < mContentOffset){
04493     aPos->mStartOffset = mContentOffset;
04494   }
04495   if (aPos->mStartOffset > (mContentOffset + mContentLength)){
04496     nsIFrame *nextInFlow;
04497 #ifdef IBMBIDI
04498     if (isOddLevel) {
04499       nextInFlow = NS_STATIC_CAST(nsIFrame*,
04500                            aPresContext->PropertyTable()->GetProperty(this,
04501                                                      nsLayoutAtoms::nextBidi));
04502     }
04503     else
04504 #endif
04505     nextInFlow = GetNextInFlow();
04506     if (!nextInFlow){
04507       NS_ASSERTION(PR_FALSE,"nsTextFrame::PeekOffset no more flow \n");
04508       return NS_ERROR_INVALID_ARG;
04509     }
04510     // undoing the RTL flipping of mPreferLeft for the delegation
04511     // to the next frame
04512     if ((eSelectCharacter == aPos->mAmount)
04513         || (eSelectWord == aPos->mAmount))
04514       aPos->mPreferLeft ^= isOddLevel;
04515     return nextInFlow->PeekOffset(aPresContext, aPos);
04516   }
04517  
04518   // XXX TODO: explain the following:
04519   //           - if this frame is the first of the last in the line according
04520   //             to the content indices, why not handle the cases of
04521   //             eSelectBeginLine/eSelectEndLine 
04522   //           - why can't we make the hand-off to the parent class' method
04523   //             before the of aPos->mStartOffset and aPos->mPreferLeft?
04524  
04525   if (aPos->mAmount == eSelectLine || aPos->mAmount == eSelectBeginLine 
04526       || aPos->mAmount == eSelectEndLine || aPos->mAmount == eSelectParagraph)
04527   {
04528       return nsFrame::PeekOffset(aPresContext, aPos);
04529   }
04530 
04531   nsAutoTextBuffer paintBuffer;
04532   nsAutoIndexBuffer indexBuffer;
04533   nsresult rv = indexBuffer.GrowTo(mContentLength + 1);
04534   if (NS_FAILED(rv)) {
04535     return rv;
04536   }
04537   PRInt32* ip = indexBuffer.mBuffer;
04538 
04539   PRInt32 textLength;
04540   nsresult result(NS_ERROR_FAILURE);
04541   aPos->mResultContent = mContent;//do this right off
04542   switch (aPos->mAmount){
04543     case eSelectNoAmount:
04544     {
04545       // Transform text from content into renderable form
04546       nsIDocument* doc = mContent->GetDocument();
04547       if (!doc) {
04548         return NS_OK;
04549       }
04550       nsTextTransformer tx(doc->GetLineBreaker(), nsnull, aPresContext);
04551       PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
04552 
04553       if (textLength)//if no renderable length, you cant park here.
04554       {
04555         aPos->mContentOffset = aPos->mStartOffset;
04556         result = NS_OK;
04557       }
04558       else
04559       {
04560         aPos->mAmount = eSelectDir;//go to "next" or previous frame based on direction not THIS frame
04561         result = GetFrameFromDirection(aPresContext, aPos);
04562         if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this)
04563           return aPos->mResultFrame->PeekOffset(aPresContext, aPos);
04564         else if (NS_FAILED(result))
04565           return result;
04566       }
04567     }
04568     break;
04569 
04570     case eSelectCharacter:
04571     {
04572       // Transform text from content into renderable form
04573       nsIDocument* doc = mContent->GetDocument();
04574       if (!doc) {
04575         return NS_OK;
04576       }
04577       nsTextTransformer tx(doc->GetLineBreaker(), nsnull, aPresContext);
04578       PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
04579 
04580       nsIFrame *frameUsed = nsnull;
04581       PRInt32 start;
04582       PRBool found = PR_TRUE;
04583 
04584       PRBool selectable;
04585       PRUint8 selectStyle;
04586 
04587       IsSelectable(&selectable, &selectStyle);
04588       if ( selectStyle == NS_STYLE_USER_SELECT_ALL )
04589         found = PR_FALSE;
04590       else
04591       {
04592 
04593   #ifdef IBMBIDI // Simon - RTL frames reverse meaning of previous and next
04594         // so that right arrow always moves to the right on screen
04595         // and left arrow always moves left
04596         if ( ((aPos->mDirection == eDirPrevious) && !isOddLevel) ||
04597              ((aPos->mDirection == eDirNext) && isOddLevel) ){
04598   #else
04599         if (aPos->mDirection == eDirPrevious){
04600   #endif
04601           aPos->mContentOffset = 0;
04602           PRInt32 i;
04603 
04604           nsAutoPRUint8Buffer clusterBuffer;
04605           rv = FillClusterBuffer(aPresContext, paintBuffer.mBuffer,
04606                                  (PRUint32)textLength, clusterBuffer);
04607           NS_ENSURE_SUCCESS(rv, rv);
04608 
04609           for (i = aPos->mStartOffset -1 - mContentOffset; i >=0;  i--){
04610             if ((ip[i] < ip[aPos->mStartOffset - mContentOffset]) &&
04611                 (clusterBuffer.mBuffer[ip[i] - mContentOffset]) &&
04612                 (! IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset])))
04613             {
04614               aPos->mContentOffset = i + mContentOffset;
04615               break;
04616             }
04617           }
04618 
04619   #ifdef SUNCTL
04620           static NS_DEFINE_CID(kLECID, NS_ULE_CID);
04621 
04622           nsCOMPtr<nsILE> ctlObj;
04623           ctlObj = do_CreateInstance(kLECID, &rv);
04624           if (NS_FAILED(rv)) {
04625             NS_WARNING("Cell based cursor movement will not be supported\n");
04626             ctlObj = nsnull;
04627           }
04628           else {
04629             PRBool  needsCTL = PR_FALSE;
04630             PRInt32 previousOffset;
04631 
04632             ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*,
04633                                                      paintBuffer.mBuffer),
04634                                  aPos->mStartOffset, -1, &needsCTL);
04635 
04636             if (needsCTL) {
04637               ctlObj->PrevCluster(NS_REINTERPRET_CAST(const PRUnichar*,
04638                                                        paintBuffer.mBuffer),
04639                                    textLength,aPos->mStartOffset,
04640                                    &previousOffset);
04641               aPos->mContentOffset = i = previousOffset;
04642             }
04643           }
04644   #endif /* SUNCTL */
04645 
04646           if (i <0){
04647             found = PR_FALSE;
04648             frameUsed = GetPrevInFlow();
04649             start = mContentOffset;
04650             aPos->mContentOffset = start;//in case next call fails we stop at this offset
04651           }
04652         }
04653   #ifdef IBMBIDI // Simon, as above 
04654         else if ( ((aPos->mDirection == eDirNext) && !isOddLevel) ||
04655                   ((aPos->mDirection == eDirPrevious) && isOddLevel) ){
04656   #else
04657         else if (aPos->mDirection == eDirNext){
04658   #endif
04659           PRInt32 i;
04660           aPos->mContentOffset = mContentLength;
04661 
04662           nsAutoPRUint8Buffer clusterBuffer;
04663           rv = FillClusterBuffer(aPresContext, paintBuffer.mBuffer,
04664                                  (PRUint32)textLength, clusterBuffer);
04665           NS_ENSURE_SUCCESS(rv, rv);
04666 
04667           for (i = aPos->mStartOffset - mContentOffset; i <= mContentLength; i++) {
04668             if ((ip[i] > ip[aPos->mStartOffset - mContentOffset]) &&
04669                 ((i == mContentLength) ||
04670                  (!IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i] - mContentOffset])) &&
04671                  (clusterBuffer.mBuffer[ip[i] - mContentOffset]))) {
04672               aPos->mContentOffset = i + mContentOffset;
04673               break;
04674             }
04675           }
04676 
04677   #ifdef SUNCTL
04678           static NS_DEFINE_CID(kLECID, NS_ULE_CID);
04679 
04680           nsCOMPtr<nsILE> ctlObj;
04681           ctlObj = do_CreateInstance(kLECID, &rv);
04682           if (NS_FAILED(rv)) {
04683             NS_WARNING("Cell based cursor movement will not be supported\n");
04684             ctlObj = nsnull;
04685           }
04686           else {
04687             PRBool needsCTL = PR_FALSE;
04688             PRInt32 nextOffset;
04689 
04690             ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*,
04691                                                      paintBuffer.mBuffer),
04692                                 aPos->mStartOffset, 0, &needsCTL);
04693 
04694             if (needsCTL) {
04695 
04696               ctlObj->NextCluster(NS_REINTERPRET_CAST(const PRUnichar*,
04697                                                       paintBuffer.mBuffer),
04698                                   textLength, aPos->mStartOffset,
04699                                   &nextOffset);
04700               aPos->mContentOffset = i = nextOffset;
04701             }
04702           }
04703   #endif /* SUNCTL */
04704 
04705   /*      if (aStartOffset == 0 && (mState & TEXT_SKIP_LEADING_WS))
04706           i--; //back up because we just skipped over some white space. why skip over the char also?
04707   */
04708           if (i > mContentLength){
04709             found = PR_FALSE;
04710             // XXX TODO: explain why in this case GetNextInFlow() is good enough,
04711             // but in the case of
04712             // aPos->mStartOffset > (mContentOffset + mContentLength)
04713             // above, we use the presentation context and get the 'nextBidi'
04714             frameUsed = GetNextInFlow();
04715             start = mContentOffset + mContentLength;
04716             aPos->mContentOffset = start;//in case next call fails we stop at this offset
04717           }
04718         }
04719       }
04720       if (!found)
04721       {
04722         result = GetFrameFromDirection(aPresContext, aPos);
04723         if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this)
04724         {
04725           // undoing the RTL flipping of mPreferLeft in the case where 
04726           // the inner call will do it itself;
04727           // note that mAmount, as well as mPreferLeft might have been modified
04728           // by the call to GetFrameFromDirection (if we are moving to another line)
04729           if (eSelectCharacter == aPos->mAmount)
04730             aPos->mPreferLeft ^= isOddLevel;
04731           result = aPos->mResultFrame->PeekOffset(aPresContext, aPos);
04732           if (NS_FAILED(result))
04733             return result;
04734         }
04735       }
04736       else
04737         aPos->mResultContent = mContent;
04738     }
04739     break;
04740 
04741     case eSelectWord:
04742     {
04743       // Transform text from content into renderable form
04744       nsIDocument* doc = mContent->GetDocument();
04745       if (!doc) {
04746         return result;
04747       }
04748 
04749       nsTextTransformer tx(doc->GetLineBreaker(),
04750                            doc->GetWordBreaker(), aPresContext);
04751 
04752       PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
04753       nsIFrame *frameUsed = nsnull;
04754       PRBool keepSearching; //if you run out of chars before you hit the end of word, maybe next frame has more text to select?
04755       PRInt32 start;
04756       PRBool found = PR_FALSE;
04757       PRBool isWhitespace, wasTransformed;
04758       PRInt32 wordLen, contentLen;
04759       PRBool wordSelectEatSpaceAfter = tx.GetWordSelectEatSpaceAfter();
04760       
04761       PRBool selectable;
04762       PRUint8 selectStyle;
04763       IsSelectable(&selectable, &selectStyle);
04764       if ( selectStyle == NS_STYLE_USER_SELECT_ALL )
04765         found = PR_FALSE;
04766       else
04767       {
04768       
04769 #ifdef IBMBIDI // Simon - RTL frames reverse meaning of previous and next
04770         // so that right arrow always moves to the right on screen
04771         // and left arrow always moves left
04772         if ( ((aPos->mDirection == eDirPrevious) && !isOddLevel) ||
04773              ((aPos->mDirection == eDirNext) && isOddLevel) ) {
04774 #else
04775         if (aPos->mDirection == eDirPrevious){
04776 #endif
04777           keepSearching = PR_TRUE;
04778           tx.Init(this, mContent, aPos->mStartOffset);
04779           aPos->mContentOffset = mContentOffset;//initialize
04780 #ifdef IBMBIDI
04781           wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1;
04782 #endif // IBMBIDI
04783           if (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace,
04784                              PR_FALSE, aPos->mIsKeyboardSelect) &&
04785             (aPos->mStartOffset - contentLen >= mContentOffset) ){
04786             if ((aPos->mEatingWS && !isWhitespace) || !aPos->mEatingWS){
04787               aPos->mContentOffset = aPos->mStartOffset - contentLen;
04788               //check for whitespace next.
04789               if (isWhitespace && aPos->mContentOffset <= mContentOffset)
04790               {
04791                 keepSearching = PR_FALSE;//reached the beginning of a word
04792                 aPos->mEatingWS = PR_FALSE;//if no real word then
04793               }
04794               else{
04795 #ifdef IBMBIDI
04796                 wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1;
04797 #endif // IBMBIDI
04798                 while (isWhitespace &&
04799                        tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen,
04800                                       &isWhitespace, PR_FALSE,
04801                                       aPos->mIsKeyboardSelect)){
04802                   aPos->mContentOffset -= contentLen;
04803                   aPos->mEatingWS = PR_TRUE;
04804 #ifdef IBMBIDI
04805                   wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1;
04806 #endif // IBMBIDI
04807                 }
04808                 aPos->mEatingWS = !isWhitespace;//nowhite space, just eat chars.
04809                 keepSearching = aPos->mContentOffset <= mContentOffset;
04810                 if (!isWhitespace){
04811                   if (!keepSearching)
04812                     found = PR_TRUE;
04813                   else
04814                     aPos->mEatingWS = PR_TRUE;
04815                 }
04816               }
04817             }
04818             else {
04819               aPos->mContentOffset = mContentLength + mContentOffset;
04820               found = PR_TRUE;
04821             }
04822           }
04823         }
04824 #ifdef IBMBIDI // Simon, as above 
04825         else if ( ((aPos->mDirection == eDirNext) && !isOddLevel) ||
04826                   ((aPos->mDirection == eDirPrevious) && isOddLevel) ) {
04827 #else
04828         else if (aPos->mDirection == eDirNext) {
04829 #endif
04830           tx.Init(this, mContent, aPos->mStartOffset );
04831           aPos->mContentOffset = mContentOffset + mContentLength;//initialize
04832 
04833 #ifdef IBMBIDI
04834           wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1;
04835 #endif // IBMBIDI
04836           if (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aPos->mIsKeyboardSelect) &&
04837             (aPos->mStartOffset + contentLen <= (mContentLength + mContentOffset))){
04838 
04839             // On some platforms (mac, unix), we want the selection to end
04840             // at the end of the word (not the beginning of the next one).
04841             if ((wordSelectEatSpaceAfter ? isWhitespace : !isWhitespace) || !aPos->mEatingWS) {
04842               aPos->mContentOffset = aPos->mStartOffset + contentLen;
04843               keepSearching = PR_TRUE;
04844               if (wordSelectEatSpaceAfter ? isWhitespace : !isWhitespace)
04845                 aPos->mEatingWS = PR_TRUE;
04846 #ifdef IBMBIDI
04847               wordLen = (mState & NS_FRAME_IS_BIDI)
04848                       ? mContentOffset + mContentLength : -1;
04849 #endif // IBMBIDI
04850               while (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aPos->mIsKeyboardSelect))
04851               {
04852                 if (wordSelectEatSpaceAfter ? !isWhitespace : aPos->mEatingWS)
04853                   break;
04854                 if (aPos->mStartOffset + contentLen >= (mContentLength + mContentOffset))
04855                   goto TryNextFrame;
04856                 if (wordSelectEatSpaceAfter ? isWhitespace : !isWhitespace)
04857                   aPos->mEatingWS = PR_TRUE;
04858                 aPos->mContentOffset += contentLen;
04859 #ifdef IBMBIDI
04860                 wordLen = (mState & NS_FRAME_IS_BIDI)
04861                         ? mContentOffset + mContentLength : -1;
04862 #endif // IBMBIDI
04863               }
04864               keepSearching = (mContentOffset + mContentLength) <= aPos->mContentOffset;
04865               if (!keepSearching)
04866                 found = PR_TRUE;
04867             }
04868             else
04869             {
04870               aPos->mContentOffset = mContentOffset;
04871               found = PR_TRUE;
04872             }
04873           } 
04874 
04875   TryNextFrame:
04876           // XXX TODO: explain why in this case GetNextInFlow() is good enough,
04877           // but in the case of
04878           // aPos->mStartOffset > (mContentOffset + mContentLength)
04879           // above, we use the presentation context and get the 'nextBidi'
04880           frameUsed = GetNextInFlow();
04881           start = 0;
04882         }
04883       }
04884       if (!found ||
04885           (aPos->mContentOffset > (mContentOffset + mContentLength)) ||
04886           (aPos->mContentOffset < mContentOffset))
04887       {
04888         aPos->mContentOffset = PR_MIN(aPos->mContentOffset, mContentOffset + mContentLength);
04889         aPos->mContentOffset = PR_MAX(aPos->mContentOffset, mContentOffset);
04890         if (wordSelectEatSpaceAfter && aPos->mDirection == eDirNext && aPos->mEatingWS) {
04891           //If we want to stop at beginning of the next word
04892           //GetFrameFromDirction should not return NS_ERROR_FAILURE at end of line
04893           aPos->mEatingWS = PR_FALSE;
04894           result = GetFrameFromDirection(aPresContext, aPos);
04895           aPos->mEatingWS = PR_TRUE;
04896         }
04897         else
04898           result = GetFrameFromDirection(aPresContext, aPos);
04899         if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this)
04900         {
04901           // undoing the RTL flipping of mPreferLeft in the case where
04902           // the inner call will do it itself;
04903           // note that mAmount, as well as mPreferLeft might have been modified
04904           // by the call to GetFrameFromDirection (if we are moving to another line)
04905           if (eSelectWord == aPos->mAmount)
04906             aPos->mPreferLeft ^= isOddLevel;
04907           if (NS_SUCCEEDED(result = aPos->mResultFrame->PeekOffset(aPresContext, aPos)))
04908             return NS_OK;//else fall through
04909           else if (aPos->mDirection == eDirNext)
04910             aPos->mContentOffset = mContentOffset + mContentLength;
04911           else
04912             aPos->mContentOffset = mContentOffset;
04913         }
04914         else 
04915           aPos->mResultContent = mContent;
04916       }
04917       else 
04918       {
04919         aPos->mResultContent = mContent;
04920       }
04921     }
04922     break;
04923 
04924 #ifdef IBMBIDI
04925     case eSelectDir:
04926       result = GetFrameFromDirection(aPresContext, aPos);
04927       if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this)
04928         return aPos->mResultFrame->PeekOffset(aPresContext, aPos);
04929       else {
04930         return result;
04931       }
04932       break;
04933 #endif
04934 
04935     default:
04936       result = NS_ERROR_FAILURE; break;
04937   }
04938 
04939   aPos->mContentOffsetEnd = aPos->mContentOffset;
04940 
04941   if (NS_FAILED(result)){
04942     aPos->mResultContent = mContent;
04943     //aPos->mContentOffset = aPos->mStartOffset;
04944     result = NS_OK;
04945   }
04946   aPos->mResultFrame = this;
04947 
04948   return result;
04949 }
04950 
04951 NS_IMETHODIMP
04952 nsTextFrame::HandleMultiplePress(nsPresContext* aPresContext, 
04953                                  nsGUIEvent*     aEvent,
04954                                  nsEventStatus*  aEventStatus)
04955 {
04956   if (DisplaySelection(aPresContext) == nsISelectionController::SELECTION_OFF) {
04957     return NS_OK;
04958   }
04959 
04960   nsMouseEvent *me = (nsMouseEvent *)aEvent;
04961   if (!me) return NS_OK;
04962 
04963   // Triple- and greater click counts are handled by nsFrame.
04964   if (me->clickCount > 2)
04965     return nsFrame::HandleMultiplePress(aPresContext, aEvent, aEventStatus);
04966 
04967   // Double-click: word selection, handled here:
04968   PRInt32 startPos = 0;
04969   PRInt32 contentOffsetEnd = 0;
04970   nsCOMPtr<nsIContent> newContent;
04971   nsresult rv = GetPosition(aPresContext, aEvent->point,
04972                             getter_AddRefs(newContent), startPos,
04973                             contentOffsetEnd);
04974   if (NS_FAILED(rv))
04975     return rv;
04976 
04977   return PeekBackwardAndForward(eSelectWord, eSelectWord, startPos,
04978                                 aPresContext, PR_FALSE);
04979 }
04980 
04981 
04982 NS_IMETHODIMP
04983 nsTextFrame::CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval)
04984 {
04985   if (!aFinished || !_retval)
04986     return NS_ERROR_NULL_POINTER;
04987   if (*aFinished)
04988     return NS_ERROR_FAILURE; //dont call with finished != false
04989   if (mContentOffset > aEndIndex)
04990     return NS_OK; //reached the end
04991   if (mContentOffset > aStartIndex)
04992     aStartIndex = mContentOffset;
04993   if (aStartIndex >= aEndIndex) //how can it be greater?? check anyway
04994     return NS_OK; //reached the end.
04995 
04996   nsresult rv ;
04997   if (aStartIndex < (mContentOffset + mContentLength))
04998   {
04999   //get the presshell
05000     nsIPresShell *shell = aContext->GetPresShell();
05001     if (!shell) 
05002       return NS_ERROR_FAILURE;
05003 
05004   //get the document
05005     nsIDocument *doc = shell->GetDocument();
05006     if (!doc)
05007       return NS_ERROR_FAILURE;
05008   //create texttransformer
05009     nsTextTransformer tx(doc->GetLineBreaker(), nsnull, aContext);
05010   //create the buffers
05011     nsAutoTextBuffer paintBuffer;
05012     nsAutoIndexBuffer indexBuffer;
05013     if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1)))
05014       return NS_ERROR_FAILURE;//bail out
05015 
05016     PRInt32 textLength;
05017     PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
05018     if (textLength)//we have something to measure?
05019     {
05020       PRInt32 start = PR_MAX(aStartIndex,mContentOffset);
05021       PRInt32 end = PR_MIN(mContentOffset + mContentLength-1, aEndIndex); //base 0 index of array
05022       while (start != end)
05023       { 
05024         if (indexBuffer.mBuffer[start] < indexBuffer.mBuffer[start+1]) //we have a rendered char!
05025         {
05026           *aFinished = PR_TRUE;//we are done bubble out.
05027           *_retval = PR_TRUE;//hit a drawn char
05028           return NS_OK;
05029         }
05030         start++;
05031       }
05032       if (start == aEndIndex)
05033       {
05034         *aFinished = PR_TRUE;
05035       }
05036     }
05037   }
05038   if (aRecurse) //recurse through the siblings.
05039   {
05040     nsIFrame *nextInFlow = this; 
05041     rv = NS_OK;
05042     while (!aFinished && nextInFlow && NS_SUCCEEDED(rv) && !*_retval) //while we havent found anything visible
05043     {
05044       nextInFlow = nextInFlow->GetNextInFlow();
05045       if (nextInFlow)
05046       {
05047         rv = nextInFlow->CheckVisibility(aContext,aStartIndex,aEndIndex,PR_FALSE,aFinished,_retval);
05048       }
05049     }
05050   }
05051   return NS_OK;
05052 }
05053 
05054 
05055 
05056 NS_IMETHODIMP
05057 nsTextFrame::GetOffsets(PRInt32 &start, PRInt32 &end) const
05058 {
05059   start = mContentOffset;
05060   end = mContentOffset+mContentLength;
05061   return NS_OK;
05062 }
05063   
05064 #define TEXT_MAX_NUM_SEGMENTS 65
05065 
05066 struct SegmentData {
05067   PRUint32  mIsWhitespace : 1;
05068   PRUint32  mContentLen : 31;  // content length
05069 
05070   PRBool  IsWhitespace() {return PRBool(mIsWhitespace);}
05071 
05072   // Get the content length. This is a running total of all
05073   // the previous segments as well
05074   PRInt32 ContentLen() {return PRInt32(mContentLen);}
05075 };
05076 
05077 struct TextRun {
05078   // Total number of characters and the accumulated content length
05079   PRInt32       mTotalNumChars, mTotalContentLen;
05080 
05081   // Words and whitespace each count as a segment
05082   PRInt32       mNumSegments;
05083 
05084   // Possible break points specified as offsets into the buffer
05085   PRInt32       mBreaks[TEXT_MAX_NUM_SEGMENTS];
05086 
05087   // Per segment data
05088   SegmentData   mSegments[TEXT_MAX_NUM_SEGMENTS];
05089 
05090   TextRun()
05091   {
05092     Reset();
05093   }
05094   
05095   void Reset()
05096   {
05097     mNumSegments = 0;
05098     mTotalNumChars = 0;
05099     mTotalContentLen = 0;
05100   }
05101 
05102   // Returns PR_TRUE if we're currently buffering text
05103   PRBool IsBuffering()
05104   {
05105     return mNumSegments > 0;
05106   }
05107 
05108   void AddSegment(PRInt32 aNumChars, PRInt32 aContentLen, PRBool aIsWhitespace)
05109   {
05110     NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow");
05111 #ifdef IBMBIDI
05112     if (mNumSegments >= TEXT_MAX_NUM_SEGMENTS) {
05113       return;
05114     }
05115 #endif // IBMBIDI
05116     mTotalNumChars += aNumChars;
05117     mBreaks[mNumSegments] = mTotalNumChars;
05118     mSegments[mNumSegments].mIsWhitespace = aIsWhitespace;
05119     mTotalContentLen += aContentLen;
05120     mSegments[mNumSegments].mContentLen = PRUint32(mTotalContentLen);
05121     mNumSegments++;
05122   }
05123 };
05124 
05125 // Transforms characters in place from ascii to Unicode
05126 static void
05127 TransformTextToUnicode(char* aText, PRInt32 aNumChars)
05128 {
05129   // Go backwards over the characters and convert them.
05130   unsigned char*  cp1 = (unsigned char*)aText + aNumChars - 1;
05131   PRUnichar*      cp2 = (PRUnichar*)aText + (aNumChars - 1);
05132   
05133   while (aNumChars-- > 0) {
05134     // XXX: If you crash here then you may see the issue described
05135     // in http://bugzilla.mozilla.org/show_bug.cgi?id=36146#c44
05136     *cp2-- = PRUnichar(*cp1--);
05137   }
05138 }
05139  
05140 PRUint32
05141 nsTextFrame::EstimateNumChars(PRUint32 aAvailableWidth,
05142                               PRUint32 aAverageCharWidth)
05143 {
05144   // Estimate the number of characters that will fit. Use 105% of the available
05145   // width divided by the average character width.
05146   // If mAveCharWidth is zero, we can fit the entire line.
05147   if (aAverageCharWidth == 0) {
05148     return PR_UINT32_MAX;
05149   }
05150 
05151   PRUint32 estimatedNumChars = aAvailableWidth / aAverageCharWidth;
05152   return estimatedNumChars + estimatedNumChars / 20;
05153 }
05154   
05155 // Replaced by precompiled CCMap (see bug 180266). To update the list
05156 // of characters, see one of files included below. As for the way
05157 // the original list of characters was obtained by Frank Tang, see bug 54467.
05158 // Updated to fix the regression (bug 263411). The list contains
05159 // characters of the following Unicode character classes : Ps, Pi, Po, Pf, Pe.
05160 // (ref.: http://www.w3.org/TR/2004/CR-CSS21-20040225/selector.html#first-letter)
05161 // Note that the file does NOT yet include non-BMP characters because 
05162 // there's no point including them without fixing the way we identify 
05163 // 'first-letter' currently working only with BMP characters.
05164 #include "punct_marks.ccmap"
05165 DEFINE_CCMAP(gPuncCharsCCMap, const);
05166   
05167 #define IsPunctuationMark(ch) (CCMAP_HAS_CHAR(gPuncCharsCCMap, ch))
05168 
05169 nsReflowStatus
05170 nsTextFrame::MeasureText(nsPresContext*          aPresContext,
05171                          const nsHTMLReflowState& aReflowState,
05172                          nsTextTransformer&       aTx,
05173                          nsILineBreaker*          aLb,
05174                          TextStyle&               aTs,
05175                          TextReflowData&          aTextData)
05176 {
05177   PRBool firstThing = PR_TRUE;
05178   nscoord maxWidth = aReflowState.availableWidth;
05179   nsLineLayout& lineLayout = *aReflowState.mLineLayout;
05180   PRInt32 contentLength = aTx.GetContentLength();
05181   PRInt32 startingOffset = aTextData.mOffset;
05182   PRInt32 prevOffset = -1;
05183   PRInt32 column = mColumn;
05184   PRInt32 prevColumn = column;
05185   nscoord prevMaxWordWidth = 0, prevAscent = 0, prevDescent = 0;
05186   PRInt32 lastWordLen = 0;
05187   PRUnichar* lastWordPtr = nsnull;
05188   PRBool  endsInWhitespace = PR_FALSE;
05189   PRBool  endsInNewline = PR_FALSE;
05190   PRBool  justDidFirstLetter = PR_FALSE;
05191   nsTextDimensions dimensions, lastWordDimensions;
05192   PRBool  measureTextRuns = PR_FALSE;
05193 
05194   if (contentLength == 0) {
05195     aTextData.mX = 0;
05196     aTextData.mAscent = 0;
05197     aTextData.mDescent = 0;
05198     return NS_FRAME_COMPLETE;
05199   }
05200 #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)
05201   // see if we have implementation for GetTextDimensions()
05202   PRUint32 hints = 0;
05203   aReflowState.rendContext->GetHints(hints);
05204   if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
05205     measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted &&
05206                       !aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing &&
05207                       aTextData.mWrapping;
05208   }
05209   // Don't measure text runs with letter spacing active, it doesn't work
05210   // it also doesn't work if we are not word-wrapping (bug 42832)
05211 #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)*/
05212   TextRun textRun;
05213   PRUint32 estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX,
05214                                                 aTs.mAveCharWidth);
05215 
05216 #ifdef IBMBIDI
05217   nsTextFrame* nextBidi = nsnull;
05218   PRInt32      start = -1, end;
05219 
05220   if (mState & NS_FRAME_IS_BIDI) {
05221     nextBidi = NS_STATIC_CAST(nsTextFrame*,
05222                               aPresContext->PropertyTable()->GetProperty(this,
05223                                                      nsLayoutAtoms::nextBidi));
05224 
05225     if (nextBidi) {
05226       if (mContentLength < 1) {
05227         mContentLength = 1;
05228       }
05229       nextBidi->GetOffsets(start, end);
05230       if (start <= mContentOffset) {
05231         nextBidi->AdjustOffsetsForBidi(mContentOffset + mContentLength, end);
05232       }
05233       else {
05234         mContentLength = start - mContentOffset;
05235       }
05236     }
05237   }
05238 #endif //IBMBIDI
05239 
05240   aTextData.mX = 0;
05241   if (aTextData.mMeasureText) {
05242     aTs.mNormalFont->GetMaxAscent(aTextData.mAscent);
05243     aTs.mNormalFont->GetMaxDescent(aTextData.mDescent);
05244   }
05245   PRBool firstWordDone = PR_FALSE;
05246   for (;;) {
05247 #ifdef IBMBIDI
05248     if (nextBidi && (mContentLength <= 0) ) {
05249       if (textRun.IsBuffering()) {
05250         // Measure the remaining text
05251         goto MeasureTextRun;
05252       }
05253       else {
05254         break;
05255       }
05256     }
05257 #endif // IBMBIDI
05258     // Get next word/whitespace from the text
05259     PRBool isWhitespace, wasTransformed;
05260     PRInt32 wordLen, contentLen;
05261     union {
05262       char*       bp1;
05263       PRUnichar*  bp2;
05264     };
05265 #ifdef IBMBIDI
05266     wordLen = start;
05267 #endif // IBMBIDI
05268 
05269     bp2 = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace,
05270                           &wasTransformed, textRun.mNumSegments == 0);
05271 
05272     // We need to set aTextData.mCanBreakBefore to true after 1st word. But we can't set 
05273     // aTextData.mCanBreakBefore without seeing the 2nd word. That's because this frame 
05274     // may only contain part of one word, the other part is in next frame. 
05275     // we don't care if first word is whitespace, that will be addressed later. 
05276     if (!aTextData.mCanBreakBefore && !firstThing && !isWhitespace) {
05277       firstWordDone = PR_TRUE;
05278     }
05279 
05280 #ifdef IBMBIDI
05281     if (nextBidi) {
05282       mContentLength -= contentLen;
05283 
05284       if (mContentLength < 0) {
05285         contentLen += mContentLength;
05286         wordLen = PR_MIN(wordLen, contentLen);
05287       }
05288     }
05289 #endif // IBMBIDI
05290     // Remember if the text was transformed
05291     if (wasTransformed) {
05292       mState |= TEXT_WAS_TRANSFORMED;
05293     }
05294     
05295     if (bp2) {
05296       if (firstWordDone) {
05297         // The first word has been processed, and 2nd word is seen 
05298         // we can set it be breakable here after.
05299          aTextData.mCanBreakBefore = PR_TRUE;
05300       }
05301     } else {
05302       if (textRun.IsBuffering()) {
05303         // Measure the remaining text
05304         goto MeasureTextRun;
05305       }
05306       else {
05307         // Advance the offset in case we just consumed a bunch of
05308         // discarded characters. Otherwise, if this is the first piece
05309         // of content for this frame we will attempt to break-before it.
05310         aTextData.mOffset += contentLen;
05311         break;
05312       }
05313     }
05314 
05315     lastWordLen = wordLen;
05316     lastWordPtr = bp2;
05317     aTextData.mInWord = PR_FALSE;
05318 
05319     // Measure the word/whitespace
05320     PRUnichar firstChar;
05321     if (aTx.TransformedTextIsAscii()) {
05322       firstChar = *bp1;
05323     } else {
05324       firstChar = *bp2;
05325     }
05326     if (isWhitespace) {
05327       if ('\n' == firstChar) {
05328         // We hit a newline. Stop looping.
05329         NS_WARN_IF_FALSE(aTs.mPreformatted, "newline w/o ts.mPreformatted");
05330         prevOffset = aTextData.mOffset;
05331         aTextData.mOffset++;
05332         endsInWhitespace = PR_TRUE;
05333         endsInNewline = PR_TRUE;
05334         break;
05335       }
05336       if (aTextData.mSkipWhitespace) {
05337         aTextData.mOffset += contentLen;
05338         aTextData.mSkipWhitespace = PR_FALSE;
05339 
05340         if (wasTransformed) {
05341           // As long as there were no discarded characters, then don't consider
05342           // skipped leading whitespace as being transformed
05343           if (wordLen == contentLen) {
05344             mState &= ~TEXT_WAS_TRANSFORMED;
05345           }
05346         }
05347 
05348         // Only set flag when we actually do skip whitespace
05349         mState |= TEXT_SKIP_LEADING_WS;
05350         continue;
05351       }
05352       firstThing = PR_FALSE;
05353 
05354       // NOTE: Even if the textRun absorbs the whitespace below, we still
05355       // want to remember that we're breakable.
05356       aTextData.mCanBreakBefore = PR_TRUE;
05357       aTextData.mFirstLetterOK = PR_FALSE;
05358  
05359       if ('\t' == firstChar) {
05360         // Expand tabs to the proper width
05361         wordLen = 8 - (7 & column);
05362         // Apply word spacing to every space derived from a tab
05363         dimensions.width = (aTs.mSpaceWidth + aTs.mWordSpacing + aTs.mLetterSpacing)*wordLen;
05364 
05365         // Because we have to expand the tab when rendering consider that
05366         // a transformation of the text
05367         mState |= TEXT_WAS_TRANSFORMED;
05368       }
05369       else if (textRun.IsBuffering()) {
05370         // Add a whitespace segment
05371         textRun.AddSegment(wordLen, contentLen, PR_TRUE);
05372         continue;
05373       }
05374       else {
05375         // Apply word spacing to every space, if there's more than one
05376         dimensions.width = wordLen*(aTs.mWordSpacing + aTs.mLetterSpacing + aTs.mSpaceWidth);// XXX simplistic
05377       }
05378 
05379       //Even if there is not enough space for this "space", we still put it 
05380       //here instead of next line
05381       prevColumn = column;
05382       column += wordLen;
05383       endsInWhitespace = PR_TRUE;
05384       prevOffset = aTextData.mOffset;
05385       aTextData.mOffset += contentLen;
05386 
05387       if (aTextData.mMeasureText) {
05388         //if we're wrapping, then don't add the whitespace width to the 
05389         // x-offset unless the whitespace will fit within maxWidth.''
05390         if (aTextData.mWrapping) {
05391           if (aTextData.mX + dimensions.width <= maxWidth) {
05392             aTextData.mX += dimensions.width;
05393           }
05394           else {
05395             // since we didn't add the trailing space width, set this flag so that 
05396             // we will not trim this non-existing space
05397             aTextData.mTrailingSpaceTrimmed = PR_TRUE;
05398             break;
05399           }
05400         }
05401         else {
05402           //if we're not wrapping, then always advance 
05403           // the x-offset regardless of maxWidth
05404           aTextData.mX += dimensions.width;
05405         }
05406       } //(aTextData.mMeasureText)
05407     }
05408     else {
05409       firstThing = PR_FALSE;
05410       aTextData.mSkipWhitespace = PR_FALSE;
05411 
05412       if (aTextData.mFirstLetterOK) {
05413         if (IsPunctuationMark(firstChar)) {
05414           if (contentLen > 1)
05415           {
05416             wordLen = 2;
05417             contentLen = 2;
05418           }
05419         }
05420         else {
05421           wordLen = 1;
05422           contentLen = 1;
05423         }
05424         justDidFirstLetter = PR_TRUE;
05425       }
05426       
05427       if (aTextData.mMeasureText) {
05428         if (measureTextRuns && !justDidFirstLetter) {
05429           // Add another word to the text run
05430           textRun.AddSegment(wordLen, contentLen, PR_FALSE);
05431 
05432           // See if we should measure the text
05433           if ((textRun.mTotalNumChars >= estimatedNumChars) ||
05434               (textRun.mNumSegments >= (TEXT_MAX_NUM_SEGMENTS - 1))) {
05435             goto MeasureTextRun;
05436           }
05437         }
05438         else {
05439           if (aTs.mSmallCaps) {
05440             MeasureSmallCapsText(aReflowState, aTs, bp2, wordLen, PR_FALSE, &dimensions);
05441           }
05442           else {
05443             // Measure just the one word
05444             if (aTx.TransformedTextIsAscii()) {
05445               aReflowState.rendContext->GetTextDimensions(bp1, wordLen, dimensions);
05446             } else {
05447               aReflowState.rendContext->GetTextDimensions(bp2, wordLen, dimensions);
05448             }
05449 #ifdef MOZ_MATHML
05450             // If GetBoundingMetrics is available, use the exact glyph metrics
05451             // for ::first-letter
05452             // XXX remove the #ifdef if GetBoundingMetrics becomes mainstream
05453             if (justDidFirstLetter) {
05454               nsresult res;
05455               nsBoundingMetrics bm;
05456               if (aTx.TransformedTextIsAscii()) {
05457                 res = aReflowState.rendContext->GetBoundingMetrics(bp1, wordLen, bm);
05458               } else {
05459                 res = aReflowState.rendContext->GetBoundingMetrics(bp2, wordLen, bm);
05460               }
05461               if (NS_SUCCEEDED(res)) {
05462                 aTextData.mAscent = dimensions.ascent = bm.ascent;
05463                 aTextData.mDescent = dimensions.descent = bm.descent;
05464               }
05465             }
05466 #endif
05467             if (aTs.mLetterSpacing) {
05468               dimensions.width += aTs.mLetterSpacing * wordLen;
05469             }
05470           }
05471           lastWordDimensions = dimensions;
05472 
05473           // See if there is room for the text
05474           if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + dimensions.width > maxWidth)) {
05475             // The text will not fit.
05476             break;
05477           }
05478           prevMaxWordWidth = aTextData.mMaxWordWidth;
05479           prevAscent = aTextData.mAscent;
05480           prevDescent =  aTextData.mDescent;
05481 
05482           aTextData.mX += dimensions.width;
05483           if (dimensions.width > aTextData.mMaxWordWidth) {
05484             aTextData.mMaxWordWidth = dimensions.width;
05485           }
05486           if (aTextData.mAscent < dimensions.ascent) {
05487             aTextData.mAscent = dimensions.ascent;
05488           }
05489           if (aTextData.mDescent < dimensions.descent) {
05490             aTextData.mDescent = dimensions.descent;
05491           }
05492 
05493           prevColumn = column;
05494           column += wordLen;
05495           endsInWhitespace = PR_FALSE;
05496           prevOffset = aTextData.mOffset;
05497           aTextData.mOffset += contentLen;
05498           if (justDidFirstLetter) {
05499             // Time to stop
05500             break;
05501           }
05502         }
05503       }
05504       else {
05505         // We didn't measure the text, but we need to update our state
05506         prevColumn = column;
05507         column += wordLen;
05508         endsInWhitespace = PR_FALSE;
05509         prevOffset = aTextData.mOffset;
05510         aTextData.mOffset += contentLen;
05511         if (justDidFirstLetter) {
05512           // Time to stop
05513           break;
05514         }
05515       }
05516     }
05517     continue;
05518 
05519   MeasureTextRun:
05520 #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)
05521   // see if we have implementation for GetTextDimensions()
05522   if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
05523     PRInt32 numCharsFit;
05524     // These calls can return numCharsFit not positioned at a break in the textRun. Beware.
05525     if (aTx.TransformedTextIsAscii()) {
05526       aReflowState.rendContext->GetTextDimensions((char*)aTx.GetWordBuffer(), textRun.mTotalNumChars,
05527                                          maxWidth - aTextData.mX,
05528                                          textRun.mBreaks, textRun.mNumSegments,
05529                                          dimensions, numCharsFit, lastWordDimensions);
05530     } else {
05531       aReflowState.rendContext->GetTextDimensions(aTx.GetWordBuffer(), textRun.mTotalNumChars,
05532                                          maxWidth - aTextData.mX,
05533                                          textRun.mBreaks, textRun.mNumSegments,
05534                                          dimensions, numCharsFit, lastWordDimensions);
05535     }
05536     // See how much of the text fit
05537     if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + dimensions.width > maxWidth)) {
05538       // None of the text fits
05539 #ifdef IBMBIDI
05540       nextBidi = nsnull;
05541 #endif // IBMBIDI
05542       break;
05543     }
05544 
05545     // Find the index of the last segment that fit
05546     PRInt32 lastSegment;
05547     if (numCharsFit >= textRun.mTotalNumChars) { // fast path, normal case
05548       NS_ASSERTION(numCharsFit == textRun.mTotalNumChars, "shouldn't overshoot");
05549       lastSegment = textRun.mNumSegments - 1;
05550     } else {
05551       for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ;
05552       NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment");
05553       // now we have textRun.mBreaks[lastSegment] >= numCharsFit
05554       /* O'Callahan XXX: This snippet together with the snippet below prevents mail from loading
05555          Justification seems to work just fine without these changes.
05556          We get into trouble in a case where lastSegment gets set to -1
05557 
05558       if (textRun.mBreaks[lastSegment] > numCharsFit) {
05559         // NOTE: this segment did not actually fit!
05560         lastSegment--;
05561       }
05562       */
05563     }
05564 
05565     /* O'Callahan XXX: This snippet together with the snippet above prevents mail from loading
05566 
05567     if (lastSegment < 0) {        
05568       // no segments fit
05569       break;
05570     } else */
05571     if (lastSegment == 0) {
05572       // Only one segment fit
05573       prevColumn = column;
05574       prevOffset = aTextData.mOffset;
05575     } else {
05576       // The previous state is for the next to last word
05577       // NOTE: The textRun data are relative to the last updated column and offset!
05578       prevColumn = column + textRun.mBreaks[lastSegment - 1];
05579       prevOffset = aTextData.mOffset + textRun.mSegments[lastSegment - 1].ContentLen();
05580     }
05581 
05582     aTextData.mX += dimensions.width;
05583     if (aTextData.mAscent < dimensions.ascent) {
05584       aTextData.mAscent = dimensions.ascent;
05585     }
05586     if (aTextData.mDescent < dimensions.descent) {
05587       aTextData.mDescent = dimensions.descent;
05588     }
05589     // this is where to backup if line-breaking happens to push the last word
05590     prevAscent = aTextData.mAscent;
05591     prevDescent = aTextData.mDescent;
05592     // we can now consider the last word since we know where to backup
05593     if (aTextData.mAscent < lastWordDimensions.ascent) {
05594       aTextData.mAscent = lastWordDimensions.ascent;
05595     }
05596     if (aTextData.mDescent < lastWordDimensions.descent) {
05597       aTextData.mDescent = lastWordDimensions.descent;
05598     }
05599 
05600     column += numCharsFit;
05601     aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen();
05602     endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace();
05603 
05604     // If all the text didn't fit, then we're done
05605     if (numCharsFit != textRun.mTotalNumChars) {
05606 #ifdef IBMBIDI
05607       nextBidi = nsnull;
05608 #endif // IBMBIDI
05609       break;
05610     }
05611 
05612 #ifdef IBMBIDI
05613     if (nextBidi && (mContentLength <= 0) ) {
05614       break;
05615     }
05616 #endif // IBMBIDI
05617 
05618     if (nsnull == bp2) {
05619       // No more text so we're all finished. Advance the offset in case the last
05620       // call to GetNextWord() discarded characters
05621       aTextData.mOffset += contentLen;
05622       break;
05623     }
05624 
05625     // Reset the number of text run segments
05626     textRun.Reset();
05627 
05628     // Estimate the remaining number of characters we think will fit
05629     estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX,
05630                                          aTs.mAveCharWidth);
05631   }
05632 #else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */
05633     int unused = -1;
05634 #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */
05635   }
05636 
05637   // If we didn't actually measure any text, then make sure it looks
05638   // like we did
05639   if (!aTextData.mMeasureText) {
05640     aTextData.mAscent = mAscent;
05641     aTextData.mDescent = mRect.height - aTextData.mAscent;
05642     aTextData.mX = mRect.width;
05643     if (mState & TEXT_TRIMMED_WS) {
05644       // Add back in the width of a space since it was trimmed away last time
05645       // NOTE: Trailing whitespace includes word and letter spacing!
05646       aTextData.mX += aTs.mSpaceWidth + aTs.mWordSpacing + aTs.mLetterSpacing;
05647     }
05648   }
05649   
05650   // Post processing logic to deal with word-breaking that spans
05651   // multiple frames.
05652   if (lineLayout.InWord()) {
05653     // We are already in a word. This means a text frame prior to this
05654     // one had a fragment of a nbword that is joined with this
05655     // frame. It also means that the prior frame already found this
05656     // frame and recorded it as part of the word.
05657 #ifdef DEBUG_WORD_WRAPPING
05658     ListTag(stdout);
05659     printf(": in word; skipping\n");
05660 #endif
05661     lineLayout.ForgetWordFrame(this);
05662   }
05663 
05664   if (!lineLayout.InWord()) {
05665     // There is no currently active word. This frame may contain the
05666     // start of one.
05667     if (endsInWhitespace) {
05668       // Nope, this frame doesn't start a word.
05669       lineLayout.ForgetWordFrames();
05670     }
05671     else if ((aTextData.mOffset == contentLength) && (prevOffset >= 0)) {
05672       // Force breakable to false when we aren't wrapping (this
05673       // guarantees that the combined word will stay together)
05674       if (!aTextData.mWrapping) {
05675         aTextData.mCanBreakBefore = PR_FALSE;
05676       }
05677 
05678       // This frame does start a word. However, there is no point
05679       // messing around with it if we are already out of room. We
05680       // always have room if we are not breakable.
05681       if (!aTextData.mCanBreakBefore || (aTextData.mX <= maxWidth)) {
05682         // There is room for this word fragment. It's possible that
05683         // this word fragment is the end of the text-run. If it's not
05684         // then we continue with the look-ahead processing.
05685         nsIFrame* next = lineLayout.FindNextText(aPresContext, this);
05686         if (nsnull != next) {
05687 #ifdef DEBUG_WORD_WRAPPING
05688           nsAutoString tmp(aTx.GetWordBuffer(), lastWordLen);
05689           ListTag(stdout);
05690           printf(": start='");
05691           fputs(NS_LossyConvertUCS2toASCII(tmp).get(), stdout);
05692           printf("' lastWordLen=%d baseWidth=%d prevOffset=%d offset=%d next=",
05693                  lastWordLen, lastWordDimensions.width, prevOffset, aTextData.mOffset);
05694           ListTag(stdout, next);
05695           printf("\n");
05696 #endif
05697           PRUnichar* pWordBuf = lastWordPtr;
05698           PRUint32   wordBufLen = aTx.GetWordBufferLength() -
05699                                   (lastWordPtr - aTx.GetWordBuffer());
05700 
05701           if (aTx.TransformedTextIsAscii()) {
05702             // The text transform buffer contains ascii characters, so
05703             // transform it to Unicode
05704             NS_ASSERTION(wordBufLen >= PRUint32(lastWordLen), "no room to transform in place");
05705             TransformTextToUnicode((char*)lastWordPtr, lastWordLen);
05706           }
05707 
05708           // Look ahead in the text-run and compute the final word
05709           // width, taking into account any style changes and stopping
05710           // at the first breakable point.
05711           if (!aTextData.mMeasureText || (lastWordDimensions.width == -1)) {
05712             // We either didn't measure any text or we measured multiple words
05713             // at once so either way we don't know lastWordDimensions. We'll have to
05714             // compute it now
05715             if (prevOffset == startingOffset) {
05716               // There's only one word, so we don't have to measure after all
05717               lastWordDimensions.width = aTextData.mX;
05718             }
05719             else if (aTs.mSmallCaps) {
05720               MeasureSmallCapsText(aReflowState, aTs, pWordBuf,
05721                                    lastWordLen, PR_FALSE, &lastWordDimensions);
05722             }
05723             else {
05724               aReflowState.rendContext->GetTextDimensions(pWordBuf, lastWordLen, lastWordDimensions);
05725               if (aTs.mLetterSpacing) {
05726                 lastWordDimensions.width += aTs.mLetterSpacing * lastWordLen;
05727               }
05728             }
05729           }
05730           nsTextDimensions wordDimensions = ComputeTotalWordDimensions(aPresContext, aLb,
05731                                                     lineLayout,
05732                                                     aReflowState, next,
05733                                                     lastWordDimensions,
05734                                                     pWordBuf,
05735                                                     lastWordLen,
05736                                                     wordBufLen,
05737                                                     aTextData.mCanBreakBefore);
05738           if (!aTextData.mCanBreakBefore || (aTextData.mX - lastWordDimensions.width + wordDimensions.width <= maxWidth)) {
05739             // The fully joined word has fit. Account for the joined
05740             // word's affect on the max-element-size here (since the
05741             // joined word is large than it's pieces, the right effect
05742             // will occur from the perspective of the container
05743             // reflowing this frame)
05744             if (wordDimensions.width > aTextData.mMaxWordWidth) {
05745               aTextData.mMaxWordWidth = wordDimensions.width;
05746             }
05747             // Now that we now that we will retain the last word, we should
05748             // account for its ascent and descent
05749             if (aTextData.mAscent < lastWordDimensions.ascent) {
05750               aTextData.mAscent = lastWordDimensions.ascent;
05751             }
05752             if (aTextData.mDescent < lastWordDimensions.descent) {
05753               aTextData.mDescent = lastWordDimensions.descent;
05754             }
05755           }
05756           else {
05757 #ifdef NOISY_REFLOW
05758             ListTag(stdout);
05759             printf(": look-ahead (didn't fit) x=%d wordWidth=%d lastWordWidth=%d\n",
05760                    aTextData.mX, wordDimensions.width, lastWordDimensions.width);
05761 #endif
05762             // The fully joined word won't fit. We need to reduce our
05763             // size by lastWordDimensions
05764             aTextData.mX -= lastWordDimensions.width;
05765             aTextData.mMaxWordWidth = prevMaxWordWidth;
05766             aTextData.mOffset = prevOffset;
05767             column = prevColumn;
05768             if (aTextData.mMeasureText) {
05769               aTextData.mAscent = prevAscent;
05770               aTextData.mDescent = prevDescent;
05771             }
05772             // else {
05773             // XXX we didn't measure the text, and so we don't know where to back up,
05774             //     we will retain our current height. However, there is a possible
05775             //     edge case that is not handled: since we just chopped the last word,
05776             //     our remaining text could have got shorter.
05777             // }
05778 #ifdef DEBUG_WORD_WRAPPING
05779             printf("  x=%d maxWordWidth=%d len=%d\n", aTextData.mX, aTextData.mMaxWordWidth,
05780                    aTextData.mOffset - startingOffset);
05781 #endif
05782             lineLayout.ForgetWordFrames();
05783           }
05784         }
05785       }
05786     }
05787   }
05788 
05789   // Inform line layout of how this piece of text ends in whitespace
05790   // (only text objects do this). Note that if x is zero then this
05791   // text object collapsed into nothingness which means it shouldn't
05792   // effect the current setting of the ends-in-whitespace flag.
05793   lineLayout.SetColumn(column);
05794   lineLayout.SetUnderstandsWhiteSpace(PR_TRUE);
05795   if (0 != aTextData.mX) {
05796     lineLayout.SetEndsInWhiteSpace(endsInWhitespace);
05797   }
05798   if (justDidFirstLetter) {
05799     lineLayout.SetFirstLetterFrame(this);
05800     lineLayout.SetFirstLetterStyleOK(PR_FALSE);
05801     mState |= TEXT_FIRST_LETTER;
05802   }
05803 
05804   // Return our reflow status
05805   nsReflowStatus rs = (aTextData.mOffset == contentLength)
05806 #ifdef IBMBIDI
05807                       || (aTextData.mOffset == start)
05808 #endif // IBMBIDI
05809     ? NS_FRAME_COMPLETE
05810     : NS_FRAME_NOT_COMPLETE;
05811   if (endsInNewline) {
05812     rs = NS_INLINE_LINE_BREAK_AFTER(rs);
05813     lineLayout.SetLineEndsInBR(PR_TRUE);
05814   }
05815   else if ((aTextData.mOffset != contentLength) && (aTextData.mOffset == startingOffset)) {
05816     // Break-before a long-word that doesn't fit here
05817     rs = NS_INLINE_LINE_BREAK_BEFORE();
05818   }
05819 
05820   return rs;
05821 }
05822 
05823 NS_IMETHODIMP
05824 nsTextFrame::Reflow(nsPresContext*          aPresContext,
05825                     nsHTMLReflowMetrics&     aMetrics,
05826                     const nsHTMLReflowState& aReflowState,
05827                     nsReflowStatus&          aStatus)
05828 {
05829   DO_GLOBAL_REFLOW_COUNT("nsTextFrame", aReflowState.reason);
05830   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
05831 #ifdef NOISY_REFLOW
05832   ListTag(stdout);
05833   printf(": BeginReflow: availableSize=%d,%d\n",
05834          aReflowState.availableWidth, aReflowState.availableHeight);
05835 #endif
05836 
05837   mState &= ~TEXT_IS_END_OF_LINE;
05838 
05839   // XXX If there's no line layout, we shouldn't even have created this
05840   // frame. This may happen if, for example, this is text inside a table
05841   // but not inside a cell. For now, just don't reflow.
05842   if (nsnull == aReflowState.mLineLayout) {
05843     // XXX Add a method to aMetrics that does this; we do it several places
05844     aMetrics.width = 0;
05845     aMetrics.height = 0;
05846     aMetrics.ascent = 0;
05847     aMetrics.descent = 0;
05848     if (aMetrics.mComputeMEW) {
05849       aMetrics.mMaxElementWidth = 0;
05850     }
05851 #ifdef MOZ_MATHML
05852     if (NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags)
05853       aMetrics.mBoundingMetrics.Clear();
05854 #endif
05855     return NS_OK;
05856   }
05857 
05858   // Get starting offset into the content
05859   PRInt32 startingOffset = 0;
05860   nsIFrame* prevInFlow = GetPrevInFlow();
05861   if (nsnull != prevInFlow) {
05862     nsTextFrame* prev = (nsTextFrame*)prevInFlow;
05863     startingOffset = prev->mContentOffset + prev->mContentLength;
05864 
05865     // If our starting offset doesn't agree with mContentOffset, then our
05866     // prev-in-flow has changed the number of characters it maps and so we
05867     // need to measure text and not try and optimize a resize reflow
05868     if (startingOffset != mContentOffset) {
05869       mState &= ~TEXT_OPTIMIZE_RESIZE;
05870     }
05871   }
05872   nsLineLayout& lineLayout = *aReflowState.mLineLayout;
05873   TextStyle ts(aPresContext, *aReflowState.rendContext, mStyleContext);
05874 
05875   if ( (mContentLength > 0) && (mState & NS_FRAME_IS_BIDI) ) {
05876     startingOffset = mContentOffset;
05877   }
05878 
05879   if (aPresContext->BidiEnabled()) {
05880     nsCharType charType = eCharType_LeftToRight;
05881     PRUint32 hints = 0;
05882     aReflowState.rendContext->GetHints(hints);
05883     charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsLayoutAtoms::charType));
05884     if ((eCharType_RightToLeftArabic == charType &&
05885         (hints & NS_RENDERING_HINT_ARABIC_SHAPING) == NS_RENDERING_HINT_ARABIC_SHAPING) ||
05886         (eCharType_RightToLeft == charType &&
05887         (hints & NS_RENDERING_HINT_BIDI_REORDERING) == NS_RENDERING_HINT_BIDI_REORDERING)) {
05888       aPresContext->SetIsBidiSystem(PR_TRUE);
05889     }
05890   }
05891 
05892   // Clear out the reflow state flags in mState (without destroying
05893   // the TEXT_BLINK_ON bit).
05894   PRBool lastTimeWeSkippedLeadingWS = 0 != (mState & TEXT_SKIP_LEADING_WS);
05895   mState &= ~TEXT_REFLOW_FLAGS;
05896   if (aReflowState.mFlags.mBlinks) {
05897     if (0 == (mState & TEXT_BLINK_ON)) {
05898       mState |= TEXT_BLINK_ON;
05899       nsBlinkTimer::AddBlinkFrame(aPresContext, this);
05900     }
05901   }
05902   else {
05903     if (0 != (mState & TEXT_BLINK_ON)) {
05904       mState &= ~TEXT_BLINK_ON;
05905       nsBlinkTimer::RemoveBlinkFrame(this);
05906     }
05907   }
05908 
05909   PRBool wrapping = (NS_STYLE_WHITESPACE_NORMAL == ts.mText->mWhiteSpace) ||
05910     (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == ts.mText->mWhiteSpace);
05911 
05912   // Set whitespace skip flag
05913   PRBool skipWhitespace = PR_FALSE;
05914   if (!ts.mPreformatted) {
05915     if (lineLayout.GetEndsInWhiteSpace()) {
05916       skipWhitespace = PR_TRUE;
05917     }
05918   }
05919 
05920   nscoord maxWidth = aReflowState.availableWidth;
05921 
05922   // Setup text transformer to transform this frames text content
05923   nsIDocument* doc = mContent->GetDocument();
05924   if (!doc) {
05925     NS_NOTREACHED("Content has no document");
05926     return NS_ERROR_FAILURE; 
05927   }
05928   PRBool forceArabicShaping = (ts.mSmallCaps ||
05929                                (0 != ts.mWordSpacing) ||
05930                                (0 != ts.mLetterSpacing) ||
05931                                ts.mJustifying);
05932   nsILineBreaker *lb = doc->GetLineBreaker();
05933   nsTextTransformer tx(lb, nsnull, aPresContext);
05934   // Keep the text in ascii if possible. Note that if we're measuring small
05935   // caps text then transform to Unicode because the helper function only
05936   // accepts Unicode text
05937   nsresult rv = tx.Init(this, mContent, startingOffset, forceArabicShaping, !ts.mSmallCaps);
05938   if (NS_OK != rv) {
05939     return rv;
05940   }
05941   //PRInt32 contentLength = tx.GetContentLength();
05942 
05943   // Set inWord to true if we are part of a previous piece of text's word. This
05944   // is only valid for one pass through the measuring loop.
05945   PRBool inWord = lineLayout.InWord() || ((nsnull != prevInFlow) && (((nsTextFrame*)prevInFlow)->mState & TEXT_FIRST_LETTER));
05946   if (inWord) {
05947     mState |= TEXT_IN_WORD;
05948   }
05949   mState &= ~TEXT_FIRST_LETTER;
05950   
05951   PRInt32 column = lineLayout.GetColumn();
05952   PRInt32 prevColumn = mColumn;
05953   mColumn = column;
05954   PRBool measureText = PR_TRUE;
05955   
05956   // We can avoid actually measuring the text if:
05957   // - this is a resize reflow
05958   // - we're not dirty (see CharacterDataChanged() function)
05959   // - we don't have a next in flow
05960   // - the previous reflow successfully reflowed all text in the
05961   //   available space
05962   // - we aren't computing the max element size (that requires we measure
05963   //   text)
05964   // - skipping leading whitespace is the same as it was the last time
05965   // - we're wrapping text and the available width is at least as big as our
05966   //   current frame width -or-
05967   //   we're not wrapping text and we're at the same column as before (this is
05968   //   an issue for preformatted tabbed text only)
05969   // - AND we aren't justified (in which case the frame width has already been tweaked and can't be used)
05970   if ((eReflowReason_Resize == aReflowState.reason) &&
05971       (0 == (mState & NS_FRAME_IS_DIRTY))) {
05972 
05973     nscoord realWidth = mRect.width;
05974     if (mState & TEXT_TRIMMED_WS) {
05975       // NOTE: Trailing whitespace includes word and letter spacing!
05976       realWidth += ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing;
05977     }
05978     if (!mNextInFlow &&
05979         (mState & TEXT_OPTIMIZE_RESIZE) &&
05980         !aMetrics.mComputeMEW &&
05981         (lastTimeWeSkippedLeadingWS == skipWhitespace) &&
05982         ((wrapping && (maxWidth >= realWidth)) ||
05983          (!wrapping && (prevColumn == column))) &&
05984 #ifdef IBMBIDI
05985         (0 == (mState & NS_FRAME_IS_BIDI) ) &&
05986 #endif // IBMBIDI
05987         !ts.mJustifying) {
05988       // We can skip measuring of text and use the value from our
05989       // previous reflow
05990       measureText = PR_FALSE;
05991 #ifdef NOISY_REFLOW
05992       printf("  => measureText=%s wrapping=%s skipWhitespace=%s",
05993              measureText ? "yes" : "no",
05994              wrapping ? "yes" : "no",
05995              skipWhitespace ? "yes" : "no");
05996       printf(" realWidth=%d maxWidth=%d\n",
05997              realWidth, maxWidth);
05998 #endif
05999     }
06000   }
06001 
06002   // Local state passed to the routines that do the actual text measurement
06003   TextReflowData  textData(startingOffset, wrapping, skipWhitespace, 
06004                            measureText, inWord, lineLayout.GetFirstLetterStyleOK(),
06005                            lineLayout.LineIsBreakable(), aMetrics.mComputeMEW, 
06006                            PR_FALSE);
06007   
06008   // Measure the text
06009   // MeasureText may set TEXT_TRIMMED_WS flag, so don't clear after the call
06010   if (ts.mFont->mSize)
06011     aStatus = MeasureText(aPresContext, aReflowState, tx, lb, ts, textData);
06012   else {
06013     textData.mX = 0;
06014     textData.mAscent = 0;
06015     textData.mDescent = 0;
06016     aStatus = NS_FRAME_COMPLETE;
06017   }
06018   if (textData.mTrailingSpaceTrimmed)
06019     mState |= TEXT_TRIMMED_WS;
06020   else
06021     mState &= ~TEXT_TRIMMED_WS;
06022 
06023   if (tx.HasMultibyte()) {
06024     mState |= TEXT_HAS_MULTIBYTE;
06025   }
06026 
06027   // Setup metrics for caller; store final max-element-size information
06028   aMetrics.width = textData.mX;
06029   if ((0 == textData.mX) && !ts.mPreformatted) {
06030     aMetrics.height = 0;
06031     aMetrics.ascent = 0;
06032     aMetrics.descent = 0;
06033   }
06034   else {
06035     aMetrics.ascent = textData.mAscent;
06036     aMetrics.descent = textData.mDescent;
06037     aMetrics.height = aMetrics.ascent + aMetrics.descent;
06038   }
06039   mAscent = aMetrics.ascent;
06040   if (!wrapping) {
06041     textData.mMaxWordWidth = textData.mX;
06042   }
06043   if (aMetrics.mComputeMEW) {
06044     aMetrics.mMaxElementWidth = textData.mMaxWordWidth;
06045   }
06046 
06047   // Set content offset and length
06048   mContentOffset = startingOffset;
06049   mContentLength = textData.mOffset - startingOffset;
06050 
06051   // Compute space and letter counts for justification, if required
06052   // Also use this one-shot path to compute the metrics needed for MathML, if required
06053   // (the flag is set only if this text happens to be inside MathML)
06054   PRBool calcMathMLMetrics = PR_FALSE;
06055   nsAutoTextBuffer* textBufferPtr = nsnull;
06056 #ifdef MOZ_MATHML
06057   nsAutoTextBuffer textBuffer;
06058   calcMathMLMetrics = (NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags) != 0;
06059   if (calcMathMLMetrics) {
06060     textBufferPtr = &textBuffer;
06061     // always use the Unicode path with MathML fonts in gfx, it is safer this way
06062     mState |= TEXT_HAS_MULTIBYTE;
06063   }
06064 #endif
06065   if (ts.mJustifying || calcMathMLMetrics) {
06066     PRIntn numJustifiableCharacter;
06067     PRInt32 textLength;
06068 
06069     // This will include a space for trailing whitespace, if any is present.
06070     // This is corrected for in nsLineLayout::TrimWhiteSpaceIn.
06071 
06072     // This work could be done in MeasureText, but it's complex to do accurately
06073     // there because of the need to repair counts when wrapped words are backed out.
06074     // So I do it via PrepareUnicodeText ... a little slower perhaps, but a lot saner,
06075     // and it localizes the counting logic to one place.
06076     PrepareUnicodeText(tx, nsnull, textBufferPtr, &textLength, PR_TRUE, &numJustifiableCharacter);
06077     lineLayout.SetTextJustificationWeights(numJustifiableCharacter, textLength - numJustifiableCharacter);
06078 
06079 #ifdef MOZ_MATHML
06080     if (calcMathMLMetrics) {
06081       SetFontFromStyle(aReflowState.rendContext, mStyleContext);
06082       nsBoundingMetrics bm;
06083       rv = aReflowState.rendContext->GetBoundingMetrics(textBuffer.mBuffer, textLength, bm);
06084       if (NS_SUCCEEDED(rv))
06085         aMetrics.mBoundingMetrics = bm;
06086       else {
06087         // Things didn't turn out well, just return the reflow metrics.
06088         aMetrics.mBoundingMetrics.ascent = aMetrics.ascent;
06089         aMetrics.mBoundingMetrics.descent = aMetrics.descent;
06090         aMetrics.mBoundingMetrics.width = aMetrics.width;
06091         aMetrics.mBoundingMetrics.rightBearing = aMetrics.width;
06092       }
06093     }
06094 #endif
06095   }
06096 
06097   nscoord maxFrameWidth  = mRect.width;
06098   nscoord maxFrameHeight = mRect.height;
06099 
06100   // For future resize reflows we would like to avoid measuring the text.
06101   // We can only do this if after this reflow we're:
06102   // - complete. If we're not complete then our desired width doesn't
06103   //   represent our total size
06104   // - we fit in the available space. We may be complete, but if we
06105   //   return a larger desired width than is available we may get pushed
06106   //   and our frame width won't get set
06107   if (NS_FRAME_IS_COMPLETE(aStatus) && !NS_INLINE_IS_BREAK(aStatus)  && 
06108       (aMetrics.width <= maxWidth)) {
06109     mState |= TEXT_OPTIMIZE_RESIZE;
06110     mRect.width = aMetrics.width;
06111   }
06112   else {
06113     mState &= ~TEXT_OPTIMIZE_RESIZE;
06114   }
06115  
06116   // If it's an incremental reflow command, then invalidate our existing
06117   // bounds.
06118   // XXX We need a finer granularity than this, but it isn't clear what
06119   // has actually changed...
06120   /*if (eReflowReason_Incremental == aReflowState.reason ||
06121       eReflowReason_Dirty == aReflowState.reason) {*/
06122     // XXX See bug 71523 We should really adjust the frames x coordinate to
06123     // a pixel boundary to solve this. 
06124     // For now we add 1 pixel to the width of the invalidated rect.
06125     // This fixes cases where the twips to pixel roundoff causes the invalidated
06126     // rect's width to be one pixel short. 
06127     nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
06128 
06129     maxFrameWidth  = PR_MAX(maxFrameWidth,  mRect.width) + onePixel; 
06130     maxFrameHeight = PR_MAX(maxFrameHeight, mRect.height);
06131     nsRect damage(0,0,maxFrameWidth,maxFrameHeight);
06132     Invalidate(damage);
06133   /*}*/
06134 
06135 
06136 #ifdef NOISY_REFLOW
06137   ListTag(stdout);
06138   printf(": desiredSize=%d,%d(a=%d/d=%d) status=%x\n",
06139          aMetrics.width, aMetrics.height, aMetrics.ascent, aMetrics.descent,
06140          aStatus);
06141 #endif
06142   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
06143   return NS_OK;
06144 }
06145 
06146 NS_IMETHODIMP
06147 nsTextFrame::CanContinueTextRun(PRBool& aContinueTextRun) const
06148 {
06149   // We can continue a text run through a text frame
06150   aContinueTextRun = PR_TRUE;
06151   return NS_OK;
06152 }
06153 
06154 NS_IMETHODIMP
06155 nsTextFrame::AdjustFrameSize(nscoord aExtraSpace, nscoord& aUsedSpace)
06156 {
06157   aUsedSpace = 0;
06158   return NS_OK;
06159 }
06160 
06161 NS_IMETHODIMP
06162 nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
06163                                     nsIRenderingContext& aRC,
06164                                     nscoord& aDeltaWidth,
06165                                     PRBool& aLastCharIsJustifiable)
06166 {
06167   aLastCharIsJustifiable = PR_FALSE;
06168   mState |= TEXT_IS_END_OF_LINE;
06169 
06170   // in some situation (for instance, in wrapping mode, last space will not 
06171   // be added to total width if it exceed maxwidth), this flag will be set 
06172   // and we shouldn't trim non-added space
06173   if (mState & TEXT_TRIMMED_WS) {
06174     aDeltaWidth = 0;
06175     return NS_OK;
06176   }
06177 
06178   nscoord dw = 0;
06179   const nsStyleText* textStyle = GetStyleText();
06180   if (mContentLength &&
06181       (NS_STYLE_WHITESPACE_PRE != textStyle->mWhiteSpace) &&
06182       (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP != textStyle->mWhiteSpace)) {
06183 
06184     // Get the text fragments that make up our content
06185     nsCOMPtr<nsITextContent> tc = do_QueryInterface(mContent);
06186     if (tc) {
06187       const nsTextFragment* frag = tc->Text();
06188       PRInt32 lastCharIndex = mContentOffset + mContentLength - 1;
06189       if (lastCharIndex < frag->GetLength()) {
06190         PRUnichar ch = frag->CharAt(lastCharIndex);
06191         if (XP_IS_SPACE(ch)) {
06192           // Get font metrics for a space so we can adjust the width by the
06193           // right amount.
06194           SetFontFromStyle(&aRC, mStyleContext);
06195 
06196           aRC.GetWidth(' ', dw);
06197           // NOTE: Trailing whitespace includes word and letter spacing!
06198           nsStyleUnit unit;
06199           unit = textStyle->mWordSpacing.GetUnit();
06200           if (eStyleUnit_Coord == unit) {
06201             dw += textStyle->mWordSpacing.GetCoordValue();
06202           }
06203           unit = textStyle->mLetterSpacing.GetUnit();
06204           if (eStyleUnit_Coord == unit) {
06205             dw += textStyle->mLetterSpacing.GetCoordValue();
06206           }
06207           aLastCharIsJustifiable = PR_TRUE;
06208         } else if (IsJustifiableCharacter(ch, IsChineseJapaneseLangGroup())) {
06209           aLastCharIsJustifiable = PR_TRUE;
06210         }
06211       }
06212     }
06213   }
06214 #ifdef NOISY_TRIM
06215   ListTag(stdout);
06216   printf(": trim => %d\n", dw);
06217 #endif
06218   if (0 != dw) {
06219     mState |= TEXT_TRIMMED_WS;
06220   }
06221   else {
06222     mState &= ~TEXT_TRIMMED_WS;
06223   }
06224   aDeltaWidth = dw;
06225   return NS_OK;
06226 }
06227 
06228 static void
06229 RevertSpacesToNBSP(PRUnichar* aBuffer, PRInt32 aWordLen)
06230 {
06231   PRUnichar* end = aBuffer + aWordLen;
06232   for (; aBuffer < end; aBuffer++) {
06233     PRUnichar ch = *aBuffer;
06234     if (ch == ' ') {
06235       *aBuffer = CH_NBSP;
06236     }
06237   }
06238 }
06239 
06240 nsTextDimensions
06241 nsTextFrame::ComputeTotalWordDimensions(nsPresContext* aPresContext,
06242                                    nsILineBreaker* aLineBreaker,
06243                                    nsLineLayout& aLineLayout,
06244                                    const nsHTMLReflowState& aReflowState,
06245                                    nsIFrame* aNextFrame,
06246                                    const nsTextDimensions& aBaseDimensions,
06247                                    PRUnichar* aWordBuf,
06248                                    PRUint32 aWordLen,
06249                                    PRUint32 aWordBufSize,
06250                                    PRBool aCanBreakBefore)
06251 {
06252   // Before we get going, convert any spaces in the current word back
06253   // to nbsp's. This keeps the breaking logic happy.
06254   RevertSpacesToNBSP(aWordBuf, (PRInt32) aWordLen);
06255 
06256   nsTextDimensions addedDimensions;
06257   PRUnichar *newWordBuf = aWordBuf;
06258   PRUint32 newWordBufSize = aWordBufSize;
06259   while (aNextFrame) {
06260     nsIContent* content = aNextFrame->GetContent();
06261 
06262 #ifdef DEBUG_WORD_WRAPPING
06263     printf("  next textRun=");
06264     nsFrame::ListTag(stdout, aNextFrame);
06265     printf("\n");
06266 #endif
06267 
06268     nsCOMPtr<nsITextContent> tc(do_QueryInterface(content));
06269     if (tc) {
06270       PRInt32 moreSize = 0;
06271       nsTextDimensions moreDimensions;
06272       moreDimensions = ComputeWordFragmentDimensions(aPresContext,
06273                                                      aLineBreaker,
06274                                                      aLineLayout,
06275                                                      aReflowState,
06276                                                      aNextFrame, content, tc,
06277                                                      &moreSize,
06278                                                      newWordBuf,
06279                                                      aWordLen,
06280                                                      newWordBufSize,
06281                                                      aCanBreakBefore);
06282       if (moreSize > 0) {
06283         //Oh, wordBuf is too small, we have to grow it
06284         newWordBufSize += moreSize;
06285         if (newWordBuf != aWordBuf) {
06286           newWordBuf = (PRUnichar*)nsMemory::Realloc(newWordBuf, sizeof(PRUnichar)*newWordBufSize);
06287           NS_ASSERTION(newWordBuf, "not enough memory");
06288         } else {
06289           newWordBuf = (PRUnichar*)nsMemory::Alloc(sizeof(PRUnichar)*newWordBufSize);
06290           NS_ASSERTION(newWordBuf, "not enough memory");
06291           if(newWordBuf)  {
06292             memcpy((void*)newWordBuf, aWordBuf, sizeof(PRUnichar)*(newWordBufSize-moreSize));
06293           }
06294         }
06295 
06296         if(newWordBuf)  {
06297           moreDimensions =
06298             ComputeWordFragmentDimensions(aPresContext, aLineBreaker,
06299                                           aLineLayout, aReflowState,
06300                                           aNextFrame, content, tc, &moreSize,
06301                                           newWordBuf, aWordLen, newWordBufSize,
06302                                           aCanBreakBefore);
06303           NS_ASSERTION((moreSize <= 0),
06304                        "ComputeWordFragmentDimensions is asking more buffer");
06305         } else {
06306           moreSize = -1;
06307           moreDimensions.Clear();
06308         }  
06309       }
06310 
06311       addedDimensions.Combine(moreDimensions);
06312 #ifdef DEBUG_WORD_WRAPPING
06313       printf("  moreWidth=%d (addedWidth=%d) stop=%c\n", moreDimensions.width,
06314              addedDimensions.width, stop?'T':'F');
06315 #endif
06316       if (moreSize == -1) {
06317         goto done;
06318       }
06319     }
06320     else {
06321       // It claimed it was text but it doesn't implement the
06322       // nsITextContent API. Therefore I don't know what to do with it
06323       // and can't look inside it. Oh well.
06324       goto done;
06325     }
06326 
06327     // Move on to the next frame in the text-run
06328     aNextFrame = aLineLayout.FindNextText(aPresContext, aNextFrame);
06329   }
06330 
06331  done:;
06332 #ifdef DEBUG_WORD_WRAPPING
06333   printf("  total word width=%d\n", aBaseDimensions.width + addedDimensions.width);
06334 #endif
06335   if (newWordBuf && (newWordBuf != aWordBuf)) {
06336     nsMemory::Free(newWordBuf);
06337   }
06338   addedDimensions.Combine(aBaseDimensions);
06339   return addedDimensions;
06340 }
06341                                     
06342 nsTextDimensions
06343 nsTextFrame::ComputeWordFragmentDimensions(nsPresContext* aPresContext,
06344                                       nsILineBreaker* aLineBreaker,
06345                                       nsLineLayout& aLineLayout,
06346                                       const nsHTMLReflowState& aReflowState,
06347                                       nsIFrame* aNextFrame,
06348                                       nsIContent* aContent,
06349                                       nsITextContent* aText,
06350                                       PRInt32* aMoreSize,
06351                                       const PRUnichar* aWordBuf,
06352                                       PRUint32& aRunningWordLen,
06353                                       PRUint32 aWordBufSize,
06354                                       PRBool aCanBreakBefore)
06355 {
06356   nsTextTransformer tx(aLineBreaker, nsnull, aPresContext);
06357   tx.Init(aNextFrame, aContent, 0);
06358   PRBool isWhitespace, wasTransformed;
06359   PRInt32 wordLen, contentLen;
06360   nsTextDimensions dimensions;
06361 #ifdef IBMBIDI
06362   if (aNextFrame->GetStateBits() & NS_FRAME_IS_BIDI) {
06363     PRInt32 nextFrameStart, nextFrameEnd;
06364     aNextFrame->GetOffsets(nextFrameStart, nextFrameEnd);
06365     wordLen = nextFrameEnd;
06366   } else {
06367     wordLen = -1;
06368   }
06369 #endif // IBMBIDI
06370   *aMoreSize = 0;
06371   PRUnichar* bp = tx.GetNextWord(PR_TRUE, &wordLen, &contentLen, &isWhitespace, &wasTransformed);
06372   if (!bp) {
06373     //empty text node, but we need to continue lookahead measurement
06374     // AND we need to remember the text frame for later so that we don't 
06375     // bother doing the word look ahead.
06376     aLineLayout.RecordWordFrame(aNextFrame);
06377     return dimensions; // 0
06378   }
06379 
06380   if (isWhitespace) {
06381     // Don't bother measuring nothing
06382     *aMoreSize = -1; // flag that we should stop now
06383     return dimensions; // 0
06384   }
06385 
06386   // We need to adjust the length by looking at the two pieces together. But if
06387   // we have to grow aWordBuf, ask the caller to do it by returning the shortfall
06388   if ((wordLen + aRunningWordLen) > aWordBufSize) {
06389     *aMoreSize = wordLen + aRunningWordLen - aWordBufSize; 
06390     return dimensions; // 0
06391   }
06392   if (contentLen < tx.GetContentLength())
06393     *aMoreSize = -1;
06394 
06395   // Convert any spaces in the current word back to nbsp's. This keeps
06396   // the breaking logic happy.
06397   RevertSpacesToNBSP(bp, wordLen);
06398 
06399   if (aCanBreakBefore) {
06400     if(wordLen > 0)
06401     {
06402       memcpy((void*)&(aWordBuf[aRunningWordLen]), bp, sizeof(PRUnichar)*wordLen);
06403       
06404       PRUint32 breakP=0;
06405       PRBool needMore=PR_TRUE;
06406       nsresult lres = aLineBreaker->Next(aWordBuf, aRunningWordLen+wordLen,
06407                                          0, &breakP, &needMore);
06408       if(NS_SUCCEEDED(lres)) 
06409         {
06410           // when we look at two pieces text together, we might decide to break
06411             // eariler than if we only look at the 2nd pieces of text
06412           if(!needMore && (breakP < (aRunningWordLen + wordLen)))
06413             {
06414               wordLen = breakP - aRunningWordLen; 
06415               if(wordLen < 0)
06416                   wordLen = 0;
06417               *aMoreSize = -1;
06418             } 
06419         }
06420       
06421       // if we don't stop, we need to extend the buf so the next one can
06422       // see this part otherwise, it does not matter since we will stop
06423       // anyway
06424       if (*aMoreSize != -1)
06425         aRunningWordLen += wordLen;
06426     }
06427   }
06428   else {
06429     // Even if the previous text fragment is not breakable, the connected pieces 
06430     // can be breakable in between. This especially true for CJK.
06431     PRBool canBreak;
06432     nsresult lres = aLineBreaker->BreakInBetween(aWordBuf, aRunningWordLen, bp, wordLen, &canBreak);
06433     if (NS_SUCCEEDED(lres) && canBreak) {
06434       wordLen = 0;
06435       *aMoreSize = -1;
06436     }
06437   }
06438 
06439   if ((*aMoreSize == -1) && (wordLen == 0))
06440     return dimensions; // 0;
06441 
06442   nsStyleContext* sc = aNextFrame->GetStyleContext();
06443   if (sc) {
06444     // Measure the piece of text. Note that we have to select the
06445     // appropriate font into the text first because the rendering
06446     // context has our font in it, not the font that aText is using.
06447     nsIRenderingContext& rc = *aReflowState.rendContext;
06448     nsCOMPtr<nsIFontMetrics> oldfm;
06449     rc.GetFontMetrics(*getter_AddRefs(oldfm));
06450 
06451     TextStyle ts(aLineLayout.mPresContext, rc, sc);
06452     if (ts.mSmallCaps) {
06453       MeasureSmallCapsText(aReflowState, ts, bp, wordLen, PR_FALSE, &dimensions);
06454     }
06455     else {
06456       rc.GetTextDimensions(bp, wordLen, dimensions);
06457       // NOTE: Don't forget to add letter spacing for the word fragment!
06458       dimensions.width += wordLen*ts.mLetterSpacing;
06459     }
06460     rc.SetFont(oldfm);
06461 
06462 #ifdef DEBUG_WORD_WRAPPING
06463     nsAutoString tmp(bp, wordLen);
06464     printf("  fragment='");
06465     fputs(NS_LossyConvertUCS2toASCII(tmp).get(), stdout);
06466     printf("' width=%d [wordLen=%d contentLen=%d ContentLength=%d]\n",
06467            dimensions.width, wordLen, contentLen, tx.GetContentLength());
06468 #endif
06469 
06470     // Remember the text frame for later so that we don't bother doing
06471     // the word look ahead.
06472     aLineLayout.RecordWordFrame(aNextFrame);
06473     return dimensions;
06474   }
06475 
06476   *aMoreSize = -1;
06477   return dimensions; // 0
06478 }
06479 
06480 #ifdef DEBUG
06481 // Translate the mapped content into a string that's printable
06482 void
06483 nsTextFrame::ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const
06484 {
06485   // Get the frames text content
06486   nsCOMPtr<nsITextContent> tc(do_QueryInterface(mContent));
06487   if (!tc) {
06488     return;
06489   }
06490 
06491   const nsTextFragment* frag = tc->Text();
06492 
06493   // Compute the total length of the text content.
06494   *aTotalContentLength = frag->GetLength();
06495 
06496   // Set current fragment and current fragment offset
06497   if (0 == mContentLength) {
06498     return;
06499   }
06500   PRInt32 fragOffset = mContentOffset;
06501   PRInt32 n = fragOffset + mContentLength;
06502   while (fragOffset < n) {
06503     PRUnichar ch = frag->CharAt(fragOffset++);
06504     if (ch == '\r') {
06505       aBuf.AppendLiteral("\\r");
06506     } else if (ch == '\n') {
06507       aBuf.AppendLiteral("\\n");
06508     } else if (ch == '\t') {
06509       aBuf.AppendLiteral("\\t");
06510     } else if ((ch < ' ') || (ch >= 127)) {
06511       aBuf.AppendLiteral("\\0");
06512       aBuf.AppendInt((PRInt32)ch, 8);
06513     } else {
06514       aBuf.Append(ch);
06515     }
06516   }
06517 }
06518 #endif
06519 
06520 nsIAtom*
06521 nsTextFrame::GetType() const
06522 {
06523   return nsLayoutAtoms::textFrame;
06524 }
06525 
06526 /* virtual */ PRBool
06527 nsTextFrame::IsEmpty()
06528 {
06529   NS_ASSERTION(!(mState & TEXT_IS_ONLY_WHITESPACE) ||
06530                !(mState & TEXT_ISNOT_ONLY_WHITESPACE),
06531                "Invalid state");
06532   
06533   // XXXldb Should this check compatibility mode as well???
06534   if (GetStyleText()->WhiteSpaceIsSignificant()) {
06535     return PR_FALSE;
06536   }
06537 
06538   if (mState & TEXT_ISNOT_ONLY_WHITESPACE) {
06539     return PR_FALSE;
06540   }
06541 
06542   if (mState & TEXT_IS_ONLY_WHITESPACE) {
06543     return PR_TRUE;
06544   }
06545   
06546   nsCOMPtr<nsITextContent> textContent( do_QueryInterface(mContent) );
06547   if (! textContent) {
06548     NS_NOTREACHED("text frame has no text content");
06549     return PR_TRUE;
06550   }
06551   
06552   PRBool isEmpty = textContent->IsOnlyWhitespace();
06553   mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
06554   return isEmpty;
06555 }
06556 
06557 #ifdef DEBUG
06558 NS_IMETHODIMP
06559 nsTextFrame::GetFrameName(nsAString& aResult) const
06560 {
06561   return MakeFrameName(NS_LITERAL_STRING("Text"), aResult);
06562 }
06563 
06564 NS_IMETHODIMP_(nsFrameState)
06565 nsTextFrame::GetDebugStateBits() const
06566 {
06567   // mask out our emptystate flags; those are just caches
06568   return nsFrame::GetDebugStateBits() &
06569     ~(TEXT_WHITESPACE_FLAGS | TEXT_REFLOW_FLAGS);
06570 }
06571 
06572 NS_IMETHODIMP
06573 nsTextFrame::List(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent) const
06574 {
06575   // Output the tag
06576   IndentBy(out, aIndent);
06577   ListTag(out);
06578 #ifdef DEBUG_waterson
06579   fprintf(out, " [parent=%p]", mParent);
06580 #endif
06581   if (HasView()) {
06582     fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, GetView()));
06583   }
06584 
06585   PRInt32 totalContentLength;
06586   nsAutoString tmp;
06587   ToCString(tmp, &totalContentLength);
06588 
06589   // Output the first/last content offset and prev/next in flow info
06590   PRBool isComplete = (mContentOffset + mContentLength) == totalContentLength;
06591   fprintf(out, "[%d,%d,%c] ", 
06592           mContentOffset, mContentLength,
06593           isComplete ? 'T':'F');
06594   
06595   if (nsnull != mNextSibling) {
06596     fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling));
06597   }
06598   nsIFrame* prevInFlow = GetPrevInFlow();
06599   if (nsnull != prevInFlow) {
06600     fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, prevInFlow));
06601   }
06602   if (nsnull != mNextInFlow) {
06603     fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow));
06604   }
06605 
06606   // Output the rect and state
06607   fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
06608   if (0 != mState) {
06609     if (mState & NS_FRAME_SELECTED_CONTENT) {
06610       fprintf(out, " [state=%08x] SELECTED", mState);
06611     } else {
06612       fprintf(out, " [state=%08x]", mState);
06613     }
06614   }
06615   fprintf(out, " [content=%p]", NS_STATIC_CAST(void*, mContent));
06616   fprintf(out, " sc=%p", NS_STATIC_CAST(void*, mStyleContext));
06617   nsIAtom* pseudoTag = mStyleContext->GetPseudoType();
06618   if (pseudoTag) {
06619     nsAutoString atomString;
06620     pseudoTag->ToString(atomString);
06621     fprintf(out, " pst=%s",
06622             NS_LossyConvertUCS2toASCII(atomString).get());
06623   }
06624   fputs("<\n", out);
06625 
06626   // Output the text
06627   aIndent++;
06628 
06629   IndentBy(out, aIndent);
06630   fputs("\"", out);
06631   fputs(NS_LossyConvertUCS2toASCII(tmp).get(), out);
06632   fputs("\"\n", out);
06633 
06634   aIndent--;
06635   IndentBy(out, aIndent);
06636   fputs(">\n", out);
06637 
06638   return NS_OK;
06639 }
06640 #endif
06641 
06642 void nsTextFrame::AdjustSelectionPointsForBidi(SelectionDetails *sdptr,
06643                                                PRInt32 textLength,
06644                                                PRBool isRTLChars,
06645                                                PRBool isOddLevel,
06646                                                PRBool isBidiSystem)
06647 {
06648   /* This adjustment is required whenever the text has been reversed by
06649    * Mozilla before rendering.
06650    *
06651    * In theory this means any text whose Bidi embedding level has been
06652    * set by the Unicode Bidi algorithm to an odd value, but this is
06653    * only true in practice on a non-Bidi platform.
06654    * 
06655    * On a Bidi platform the situation is more complicated because the
06656    * platform will automatically reverse right-to-left characters; so
06657    * Mozilla reverses text whose natural directionality is the opposite
06658    * of its embedding level: right-to-left characters whose Bidi
06659    * embedding level is even (e.g. Visual Hebrew) or left-to-right and
06660    * neutral characters whose Bidi embedding level is odd (e.g. English
06661    * text with <bdo dir="rtl">).
06662    *
06663    * The following condition is accordingly an optimization of
06664    *  if ( (!isBidiSystem && isOddLevel) ||
06665    *       (isBidiSystem &&
06666    *        ((isRTLChars && !isOddLevel) ||
06667    *         (!isRTLChars && isOddLevel))))
06668    */
06669   if (isOddLevel ^ (isRTLChars && isBidiSystem)) {
06670 
06671     PRInt32 swap  = sdptr->mStart;
06672     sdptr->mStart = textLength - sdptr->mEnd;
06673     sdptr->mEnd   = textLength - swap;
06674 
06675     // temp fix for 75026 crasher untill we fix the bidi code
06676     // the above bidi code cause mStart < 0 in some case
06677     // the problem is we have whitespace compression code in 
06678     // nsTextTransformer which cause mEnd > textLength
06679     NS_ASSERTION((sdptr->mStart >= 0) , "mStart >= 0");
06680     if(sdptr->mStart < 0 )
06681       sdptr->mStart = 0;
06682 
06683     NS_ASSERTION((sdptr->mEnd >= 0) , "mEnd >= 0");
06684     if(sdptr->mEnd < 0 )
06685       sdptr->mEnd = 0;
06686 
06687     NS_ASSERTION((sdptr->mStart <= sdptr->mEnd), "mStart <= mEnd");
06688     if(sdptr->mStart > sdptr->mEnd)
06689       sdptr->mEnd = sdptr->mStart;
06690   }
06691   
06692   return;
06693 }
06694 
06695 void
06696 nsTextFrame::AdjustOffsetsForBidi(PRInt32 aStart, PRInt32 aEnd)
06697 {
06698   AddStateBits(NS_FRAME_IS_BIDI);
06699   SetOffsets(aStart, aEnd);
06700 }
06701 
06702 void
06703 nsTextFrame::SetOffsets(PRInt32 aStart, PRInt32 aEnd)
06704 {
06705   mContentOffset = aStart;
06706   mContentLength = aEnd - aStart;
06707 }