backing out bug 347743 due to major crasher in 386332
[mozilla-central.git] / layout / generic / nsTextFrame.cpp
blob762ee71f8aba3274f8f9907baf9ac785d73ce441
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Robert O'Callahan <roc+moz@cs.cmu.edu>
24 * Roger B. Sidje <rbs@maths.uq.edu.au>
25 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Prabhat Hegde <prabhat.hegde@sun.com>
27 * Tomi Leppikangas <tomi.leppikangas@oulu.fi>
28 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
29 * Daniel Glazman <glazman@netscape.com>
30 * Neil Deakin <neil@mozdevgroup.com>
31 * Masayuki Nakano <masayuki@d-toybox.com>
32 * Mats Palmgren <mats.palmgren@bredband.net>
33 * Uri Bernstein <uriber@gmail.com>
35 * Alternatively, the contents of this file may be used under the terms of
36 * either of the GNU General Public License Version 2 or later (the "GPL"),
37 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
38 * in which case the provisions of the GPL or the LGPL are applicable instead
39 * of those above. If you wish to allow use of your version of this file only
40 * under the terms of either the GPL or the LGPL, and not to allow others to
41 * use your version of this file under the terms of the MPL, indicate your
42 * decision by deleting the provisions above and replace them with the notice
43 * and other provisions required by the GPL or the LGPL. If you do not delete
44 * the provisions above, a recipient may use your version of this file under
45 * the terms of any one of the MPL, the GPL or the LGPL.
47 * ***** END LICENSE BLOCK ***** */
49 /* rendering object for textual content of elements */
51 #include "nsCOMPtr.h"
52 #include "nsHTMLParts.h"
53 #include "nsCRT.h"
54 #include "nsSplittableFrame.h"
55 #include "nsLineLayout.h"
56 #include "nsString.h"
57 #include "nsUnicharUtils.h"
58 #include "nsPresContext.h"
59 #include "nsIContent.h"
60 #include "nsStyleConsts.h"
61 #include "nsStyleContext.h"
62 #include "nsCoord.h"
63 #include "nsIFontMetrics.h"
64 #include "nsIRenderingContext.h"
65 #include "nsIPresShell.h"
66 #include "nsITimer.h"
67 #include "prtime.h"
68 #include "nsVoidArray.h"
69 #include "prprf.h"
70 #include "nsIDOMText.h"
71 #include "nsIDocument.h"
72 #include "nsIDeviceContext.h"
73 #include "nsICaret.h"
74 #include "nsCSSPseudoElements.h"
75 #include "nsILineBreaker.h"
76 #include "nsCompatibility.h"
77 #include "nsCSSColorUtils.h"
78 #include "nsLayoutUtils.h"
79 #include "nsDisplayList.h"
80 #include "nsFrame.h"
81 #include "nsTextTransformer.h"
83 #include "nsTextFragment.h"
84 #include "nsGkAtoms.h"
85 #include "nsFrameSelection.h"
86 #include "nsISelection.h"
87 #include "nsIDOMRange.h"
88 #include "nsILookAndFeel.h"
89 #include "nsCSSRendering.h"
90 #include "nsContentUtils.h"
92 #include "nsILineIterator.h"
94 #include "nsCompressedCharMap.h"
96 #include "nsIServiceManager.h"
97 #ifdef ACCESSIBILITY
98 #include "nsIAccessible.h"
99 #include "nsIAccessibilityService.h"
100 #endif
101 #include "nsGUIEvent.h"
102 #include "nsAutoPtr.h"
103 #include "nsStyleSet.h"
105 #include "nsBidiFrames.h"
106 #include "nsBidiPresUtils.h"
107 #include "nsBidiUtils.h"
108 #include "nsTextFrameTextRunCache.h"
110 nsresult
111 nsTextFrameTextRunCache::Init() {
112 return NS_OK;
115 void
116 nsTextFrameTextRunCache::Shutdown() {
119 #ifdef SUNCTL
120 #include "nsILE.h"
121 static NS_DEFINE_CID(kLECID, NS_ULE_CID);
122 #endif /* SUNCTL */
124 #ifdef NS_DEBUG
125 #undef NOISY_BLINK
126 #undef DEBUG_WORD_WRAPPING
127 #undef NOISY_REFLOW
128 #undef NOISY_TRIM
129 #else
130 #undef NOISY_BLINK
131 #undef DEBUG_WORD_WRAPPING
132 #undef NOISY_REFLOW
133 #undef NOISY_TRIM
134 #endif
136 // #define DEBUGWORDJUMP
138 #define kSZLIG 0x00DF
139 //----------------------------------------------------------------------
141 #define TEXT_BUF_SIZE 100
143 //----------------------------------------
145 struct nsAutoIndexBuffer;
146 struct nsAutoPRUint8Buffer;
148 class nsTextStyle {
149 public:
150 const nsStyleFont* mFont;
151 const nsStyleText* mText;
152 nsIFontMetrics* mNormalFont;
153 nsIFontMetrics* mSmallFont;
154 nsIFontMetrics* mLastFont;
155 PRBool mSmallCaps;
156 nscoord mWordSpacing;
157 nscoord mLetterSpacing;
158 nscoord mSpaceWidth;
159 nscoord mAveCharWidth;
160 PRBool mJustifying;
161 PRBool mPreformatted;
162 PRInt32 mNumJustifiableCharacterToRender;
163 PRInt32 mNumJustifiableCharacterToMeasure;
164 nscoord mExtraSpacePerJustifiableCharacter;
165 PRInt32 mNumJustifiableCharacterReceivingExtraJot;
167 nsTextStyle(nsPresContext* aPresContext,
168 nsIRenderingContext& aRenderingContext,
169 nsStyleContext* sc);
171 ~nsTextStyle();
174 // Contains extra style data needed only for painting (not reflowing)
175 class nsTextPaintStyle : public nsTextStyle {
176 public:
177 enum{
178 eNormalSelection =
179 nsISelectionController::SELECTION_NORMAL,
180 eIMESelections =
181 nsISelectionController::SELECTION_IME_RAWINPUT |
182 nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT |
183 nsISelectionController::SELECTION_IME_CONVERTEDTEXT |
184 nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT,
185 eAllSelections =
186 eNormalSelection | eIMESelections
189 const nsStyleColor* mColor;
191 nsTextPaintStyle(nsPresContext* aPresContext,
192 nsIRenderingContext& aRenderingContext,
193 nsStyleContext* aStyleContext,
194 nsIContent* aContent,
195 PRInt16 aSelectionStatus);
196 ~nsTextPaintStyle();
198 nscolor GetTextColor();
199 void GetSelectionColors(nscolor* aForeColor,
200 nscolor* aBackColor,
201 PRBool* aBackIsTransparent);
202 void GetIMESelectionColors(SelectionType aSelectionType,
203 nscolor* aForeColor,
204 nscolor* aBackColor,
205 PRBool* aBackIsTransparent);
206 // if this returns PR_FALSE, we don't need to draw underline.
207 PRBool GetIMEUnderline(SelectionType aSelectionType,
208 nscolor* aLineColor,
209 float* aRelativeSize);
210 protected:
211 nsPresContext* mPresContext;
212 nsStyleContext* mStyleContext;
213 nsIContent* mContent;
214 PRInt16 mSelectionStatus; // see nsIDocument.h SetDisplaySelection()
216 // Common colors
217 PRBool mInitCommonColors;
219 PRInt32 mSufficientContrast;
220 nscolor mFrameBackgroundColor;
222 // Selection colors
223 PRBool mInitSelectionColors;
225 nscolor mSelectionTextColor;
226 nscolor mSelectionBGColor;
227 PRBool mSelectionBGIsTransparent;
229 // IME selection colors and underline info
230 struct nsIMEColor {
231 PRBool mInit;
232 nscolor mTextColor;
233 nscolor mBGColor;
234 nscolor mBGIsTransparent;
235 nscolor mUnderlineColor;
237 nsIMEColor mIMEColor[4];
238 // indexs
239 enum {
240 eIndexRawInput = 0,
241 eIndexSelRawText,
242 eIndexConvText,
243 eIndexSelConvText
245 float mIMEUnderlineRelativeSize;
247 // Color initializations
248 PRBool InitCommonColors();
249 PRBool InitSelectionColors();
251 nsIMEColor* GetIMEColor(SelectionType aSelectionType);
252 PRBool InitIMEColors(SelectionType aSelectionType, nsIMEColor*);
254 PRBool EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor);
256 nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor,
257 nscolor aBackColor);
260 class nsTextFrame : public nsFrame {
261 public:
262 nsTextFrame(nsStyleContext* aContext) : nsFrame(aContext)
264 NS_ASSERTION(mContentOffset == 0, "Bogus content offset");
265 NS_ASSERTION(mContentLength == 0, "Bogus content length");
268 // nsIFrame
269 NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
270 const nsRect& aDirtyRect,
271 const nsDisplayListSet& aLists);
273 void PaintText(nsIRenderingContext& aRenderingContext, nsPoint aPt);
275 NS_IMETHOD Init(nsIContent* aContent,
276 nsIFrame* aParent,
277 nsIFrame* aPrevInFlow);
279 virtual void Destroy();
281 NS_IMETHOD GetCursor(const nsPoint& aPoint,
282 nsIFrame::Cursor& aCursor);
284 NS_IMETHOD CharacterDataChanged(nsPresContext* aPresContext,
285 nsIContent* aChild,
286 PRBool aAppend);
288 virtual nsIFrame* GetNextContinuation() const {
289 return mNextContinuation;
291 NS_IMETHOD SetNextContinuation(nsIFrame* aNextContinuation) {
292 NS_ASSERTION (!aNextContinuation || GetType() == aNextContinuation->GetType(),
293 "setting a next continuation with incorrect type!");
294 NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this),
295 "creating a loop in continuation chain!");
296 mNextContinuation = aNextContinuation;
297 if (aNextContinuation)
298 aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
299 return NS_OK;
301 virtual nsIFrame* GetNextInFlowVirtual() const { return GetNextInFlow(); }
302 nsIFrame* GetNextInFlow() const {
303 return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
304 mNextContinuation : nsnull;
306 NS_IMETHOD SetNextInFlow(nsIFrame* aNextInFlow) {
307 NS_ASSERTION (!aNextInFlow || GetType() == aNextInFlow->GetType(),
308 "setting a next in flow with incorrect type!");
309 NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this),
310 "creating a loop in continuation chain!");
311 mNextContinuation = aNextInFlow;
312 if (aNextInFlow)
313 aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
314 return NS_OK;
316 virtual nsIFrame* GetLastInFlow() const;
317 virtual nsIFrame* GetLastContinuation() const;
319 virtual nsSplittableType GetSplittableType() const {
320 return NS_FRAME_SPLITTABLE;
324 * Get the "type" of the frame
326 * @see nsGkAtoms::textFrame
328 virtual nsIAtom* GetType() const;
330 virtual PRBool IsFrameOfType(PRUint32 aFlags) const
332 // Set the frame state bit for text frames to mark them as replaced.
333 // XXX kipp: temporary
334 return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced |
335 nsIFrame::eLineParticipant));
338 #ifdef DEBUG
339 NS_IMETHOD List(FILE* out, PRInt32 aIndent) const;
340 NS_IMETHOD GetFrameName(nsAString& aResult) const;
341 NS_IMETHOD_(nsFrameState) GetDebugStateBits() const ;
342 #endif
344 NS_IMETHOD GetPositionHelper(const nsPoint& aPoint,
345 nsIContent ** aNewContent,
346 PRInt32& aContentOffset,
347 PRInt32& aContentOffsetEnd);
349 virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint);
351 NS_IMETHOD GetPositionSlowly(nsIRenderingContext * aRendContext,
352 const nsPoint& aPoint,
353 nsIContent ** aNewContent,
354 PRInt32& aOffset);
357 NS_IMETHOD SetSelected(nsPresContext* aPresContext,
358 nsIDOMRange *aRange,
359 PRBool aSelected,
360 nsSpread aSpread);
362 virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset);
363 virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset);
364 virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect,
365 PRInt32* aOffset, PRBool* aSawBeforeType);
367 NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval);
369 NS_IMETHOD GetOffsets(PRInt32 &start, PRInt32 &end)const;
371 virtual void AdjustOffsetsForBidi(PRInt32 start, PRInt32 end);
373 NS_IMETHOD GetPointFromOffset(nsPresContext* inPresContext,
374 nsIRenderingContext* inRendContext,
375 PRInt32 inOffset,
376 nsPoint* outPoint);
378 NS_IMETHOD GetChildFrameContainingOffset(PRInt32 inContentOffset,
379 PRBool inHint,
380 PRInt32* outFrameContentOffset,
381 nsIFrame* *outChildFrame);
383 virtual PRBool IsVisibleInSelection(nsISelection* aSelection);
385 virtual PRBool IsEmpty();
386 virtual PRBool IsSelfEmpty() { return IsEmpty(); }
389 * @return PR_TRUE if this text frame ends with a newline character. It
390 * should return PR_FALSE if this is not a text frame.
392 virtual PRBool HasTerminalNewline() const;
394 #ifdef ACCESSIBILITY
395 NS_IMETHOD GetAccessible(nsIAccessible** aAccessible);
396 #endif
398 // nsIHTMLReflow
399 virtual void MarkIntrinsicWidthsDirty();
400 virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
401 virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
402 virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
403 InlineMinWidthData *aData);
404 virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
405 InlinePrefWidthData *aData);
406 virtual nsSize ComputeSize(nsIRenderingContext *aRenderingContext,
407 nsSize aCBSize, nscoord aAvailableWidth,
408 nsSize aMargin, nsSize aBorder, nsSize aPadding,
409 PRBool aShrinkWrap);
410 NS_IMETHOD Reflow(nsPresContext* aPresContext,
411 nsHTMLReflowMetrics& aMetrics,
412 const nsHTMLReflowState& aReflowState,
413 nsReflowStatus& aStatus);
414 virtual PRBool CanContinueTextRun() const;
415 NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext,
416 nsIRenderingContext& aRC,
417 nscoord& aDeltaWidth,
418 PRBool& aLastCharIsJustifiable);
420 struct TextReflowData {
421 PRInt32 mX; // OUT
422 PRInt32 mOffset; // IN/OUT How far along we are in the content
423 nscoord mAscent; // OUT
424 nscoord mDescent; // OUT
425 PRPackedBool mWrapping; // IN
426 PRPackedBool mSkipWhitespace; // IN
427 PRPackedBool mMeasureText; // IN
428 PRPackedBool mInWord; // IN
429 PRPackedBool mFirstLetterOK; // IN
430 PRPackedBool mCanBreakBefore; // IN
431 PRPackedBool mTrailingSpaceTrimmed; // IN/OUT
433 TextReflowData(PRInt32 aStartingOffset,
434 PRBool aWrapping,
435 PRBool aSkipWhitespace,
436 PRBool aMeasureText,
437 PRBool aInWord,
438 PRBool aFirstLetterOK,
439 PRBool aCanBreakBefore,
440 PRBool aTrailingSpaceTrimmed)
441 : mX(0),
442 mOffset(aStartingOffset),
443 mAscent(0),
444 mDescent(0),
445 mWrapping(aWrapping),
446 mSkipWhitespace(aSkipWhitespace),
447 mMeasureText(aMeasureText),
448 mInWord(aInWord),
449 mFirstLetterOK(aFirstLetterOK),
450 mCanBreakBefore(aCanBreakBefore),
451 mTrailingSpaceTrimmed(aTrailingSpaceTrimmed)
455 nsIDocument* GetDocument(nsPresContext* aPresContext);
457 void PrepareUnicodeText(nsTextTransformer& aTransformer,
458 nsAutoIndexBuffer* aIndexBuffer,
459 nsAutoTextBuffer* aTextBuffer,
460 PRInt32* aTextLen,
461 PRBool aForceArabicShaping = PR_FALSE,
462 PRIntn* aJustifiableCharCount = nsnull,
463 PRBool aRemoveMultipleTrimmedWS = PR_FALSE);
464 void ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext,
465 nsTextStyle& aTextStyle,
466 PRUnichar* aBuffer, PRInt32 aLength, PRInt32 aNumJustifiableCharacter);
468 void SetupTextRunDirection(nsPresContext* aPresContext, nsIRenderingContext* aRenderingContext);
471 * @param aRightToLeftText whether the rendering context is reversing text
472 * using its native right-to-left capability
474 void PaintTextDecorations(nsIRenderingContext& aRenderingContext,
475 nsStyleContext* aStyleContext,
476 nsPresContext* aPresContext,
477 nsTextPaintStyle& aStyle,
478 nscoord aX, nscoord aY, nscoord aWidth,
479 PRBool aRightToLeftText,
480 PRUnichar* aText = nsnull,
481 SelectionDetails *aDetails = nsnull,
482 PRUint32 aIndex = 0,
483 PRUint32 aLength = 0,
484 const nscoord* aSpacing = nsnull);
486 void PaintTextSlowly(nsPresContext* aPresContext,
487 nsIRenderingContext& aRenderingContext,
488 nsStyleContext* aStyleContext,
489 nsTextPaintStyle& aStyle,
490 nscoord aX, nscoord aY);
492 // The passed-in rendering context must have its color set to the color the
493 // text should be rendered in.
495 * @param aRightToLeftText whether the rendering context is reversing text
496 * using its native right-to-left capability
498 void RenderString(nsIRenderingContext& aRenderingContext,
499 nsStyleContext* aStyleContext,
500 nsPresContext* aPresContext,
501 nsTextPaintStyle& aStyle,
502 PRBool aRightToLeftText,
503 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
504 nscoord aX, nscoord aY,
505 nscoord aWidth,
506 SelectionDetails *aDetails = nsnull);
508 void MeasureSmallCapsText(nsIRenderingContext* aRenderingContext,
509 nsTextStyle& aStyle,
510 PRUnichar* aWord,
511 PRInt32 aWordLength,
512 PRBool aIsEndOfFrame,
513 nsTextDimensions* aDimensionsResult);
515 PRUint32 EstimateNumChars(PRUint32 aAvailableWidth,
516 PRUint32 aAverageCharWidth);
518 nsReflowStatus MeasureText(nsPresContext* aPresContext,
519 const nsHTMLReflowState& aReflowState,
520 nsTextTransformer& aTx,
521 nsTextStyle& aTs,
522 TextReflowData& aTextData);
524 void GetTextDimensions(nsIRenderingContext& aRenderingContext,
525 nsTextStyle& aStyle,
526 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
527 nsTextDimensions* aDimensionsResult);
529 //this returns the index into the PAINTBUFFER of the x coord aWidth(based on 0 as far left)
530 //also note: this is NOT added to mContentOffset since that would imply that this return is
531 //meaningful to content yet. use index buffer from prepareunicodestring to find the content offset.
532 PRInt32 GetLengthSlowly(nsIRenderingContext& aRenderingContext,
533 nsTextStyle& aStyle,
534 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
535 nscoord aWidth);
537 // REVIEW: There is absolute no reason why IsTextInSelection should depend
538 // on a rendering context, and I've refactored the code so it doesn't.
539 PRBool IsTextInSelection();
541 nsresult GetTextInfoForPainting(nsPresContext* aPresContext,
542 nsIPresShell** aPresShell,
543 nsISelectionController** aSelectionController,
544 PRBool& aDisplayingSelection,
545 PRBool& aIsPaginated,
546 PRBool& aIsSelected,
547 PRBool& aHideStandardSelection,
548 PRInt16& aSelectionValue);
550 nsresult GetSelectionStatus(nsPresContext* aPresContext,
551 PRInt16& aSelectionValue);
553 void PaintUnicodeText(nsPresContext* aPresContext,
554 nsIRenderingContext& aRenderingContext,
555 nsStyleContext* aStyleContext,
556 nsTextPaintStyle& aStyle,
557 nscoord dx, nscoord dy);
559 void PaintAsciiText(nsPresContext* aPresContext,
560 nsIRenderingContext& aRenderingContext,
561 nsStyleContext* aStyleContext,
562 nsTextPaintStyle& aStyle,
563 nscoord dx, nscoord dy);
565 #ifdef DEBUG
566 void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const;
567 #endif
569 PRInt32 GetContentOffset() { return mContentOffset; }
570 PRInt32 GetContentLength() { return mContentLength; }
572 protected:
573 virtual ~nsTextFrame();
575 nsIFrame* mNextContinuation;
576 PRInt32 mContentOffset;
577 PRInt32 mContentLength;
578 PRInt32 mColumn;
579 nscoord mAscent;
580 //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
581 PRInt32 GetTextDimensionsOrLength(nsIRenderingContext& aRenderingContext,
582 nsTextStyle& aStyle,
583 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
584 nsTextDimensions* aDimensionsResult,
585 PRBool aGetTextDimensions/* true=get dimensions false = return length up to aDimensionsResult->width size*/);
586 nsresult GetContentAndOffsetsForSelection(nsPresContext* aPresContext,nsIContent **aContent, PRInt32 *aOffset, PRInt32 *aLength);
588 void AdjustSelectionPointsForBidi(SelectionDetails *sdptr,
589 PRInt32 textLength,
590 PRBool isRTLChars,
591 PRBool isOddLevel,
592 PRBool isBidiSystem);
594 void SetOffsets(PRInt32 start, PRInt32 end);
596 PRBool IsChineseJapaneseLangGroup();
597 PRBool IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ);
599 nsresult FillClusterBuffer(nsPresContext *aPresContext, const PRUnichar *aText,
600 PRUint32 aLength, nsAutoPRUint8Buffer& aClusterBuffer);
603 //----------------------------------------
605 // checks to see if the text can be lightened..
606 // text is darkend
607 inline PRBool CanDarken(nsPresContext* aPresContext)
609 PRBool darken;
611 if (aPresContext->GetBackgroundColorDraw()) {
612 darken = PR_FALSE;
613 } else {
614 if (aPresContext->GetBackgroundImageDraw()) {
615 darken = PR_FALSE;
616 } else {
617 darken = PR_TRUE;
621 return darken;
625 struct nsAutoIndexBuffer {
626 nsAutoIndexBuffer();
627 ~nsAutoIndexBuffer();
629 nsresult GrowTo(PRInt32 aAtLeast);
631 PRInt32* mBuffer;
632 PRInt32 mBufferLen;
633 PRInt32 mAutoBuffer[TEXT_BUF_SIZE];
636 nsAutoIndexBuffer::nsAutoIndexBuffer()
637 : mBuffer(mAutoBuffer),
638 mBufferLen(TEXT_BUF_SIZE)
640 #ifdef DEBUG
641 memset(mAutoBuffer, 0xdd, sizeof(mAutoBuffer));
642 #endif
645 nsAutoIndexBuffer::~nsAutoIndexBuffer()
647 if (mBuffer && (mBuffer != mAutoBuffer)) {
648 delete [] mBuffer;
652 nsresult
653 nsAutoIndexBuffer::GrowTo(PRInt32 aAtLeast)
655 if (aAtLeast > mBufferLen)
657 PRInt32 newSize = mBufferLen * 2;
658 if (newSize < mBufferLen + aAtLeast) {
659 newSize = mBufferLen * 2 + aAtLeast;
661 PRInt32* newBuffer = new PRInt32[newSize];
662 if (!newBuffer) {
663 return NS_ERROR_OUT_OF_MEMORY;
665 #ifdef DEBUG
666 memset(newBuffer, 0xdd, sizeof(PRInt32) * newSize);
667 #endif
668 memcpy(newBuffer, mBuffer, sizeof(PRInt32) * mBufferLen);
669 if (mBuffer != mAutoBuffer) {
670 delete [] mBuffer;
672 mBuffer = newBuffer;
673 mBufferLen = newSize;
675 return NS_OK;
678 struct nsAutoPRUint8Buffer {
679 nsAutoPRUint8Buffer();
680 ~nsAutoPRUint8Buffer();
682 nsresult GrowTo(PRInt32 aAtLeast);
684 PRUint8* mBuffer;
685 PRInt32 mBufferLen;
686 PRUint8 mAutoBuffer[TEXT_BUF_SIZE];
689 nsAutoPRUint8Buffer::nsAutoPRUint8Buffer()
690 : mBuffer(mAutoBuffer),
691 mBufferLen(TEXT_BUF_SIZE)
693 #ifdef DEBUG
694 memset(mAutoBuffer, 0xdd, sizeof(mAutoBuffer));
695 #endif
698 nsAutoPRUint8Buffer::~nsAutoPRUint8Buffer()
700 if (mBuffer && (mBuffer != mAutoBuffer)) {
701 delete [] mBuffer;
705 nsresult
706 nsAutoPRUint8Buffer::GrowTo(PRInt32 aAtLeast)
708 if (aAtLeast > mBufferLen)
710 PRInt32 newSize = mBufferLen * 2;
711 if (newSize < mBufferLen + aAtLeast) {
712 newSize = mBufferLen * 2 + aAtLeast;
714 PRUint8* newBuffer = new PRUint8[newSize];
715 if (!newBuffer) {
716 return NS_ERROR_OUT_OF_MEMORY;
718 #ifdef DEBUG
719 memset(newBuffer, 0xdd, sizeof(PRUint8) * newSize);
720 #endif
721 memcpy(newBuffer, mBuffer, sizeof(PRUint8) * mBufferLen);
722 if (mBuffer != mAutoBuffer) {
723 delete [] mBuffer;
725 mBuffer = newBuffer;
726 mBufferLen = newSize;
728 return NS_OK;
732 //----------------------------------------------------------------------
734 // Helper class for managing blinking text
736 class nsBlinkTimer : public nsITimerCallback
738 public:
739 nsBlinkTimer();
740 virtual ~nsBlinkTimer();
742 NS_DECL_ISUPPORTS
744 void AddFrame(nsIFrame* aFrame);
746 PRBool RemoveFrame(nsIFrame* aFrame);
748 PRInt32 FrameCount();
750 void Start();
752 void Stop();
754 NS_DECL_NSITIMERCALLBACK
756 static nsresult AddBlinkFrame(nsPresContext* aPresContext, nsIFrame* aFrame);
757 static nsresult RemoveBlinkFrame(nsIFrame* aFrame);
759 static PRBool GetBlinkIsOff() { return sState == 3; }
761 protected:
763 nsCOMPtr<nsITimer> mTimer;
764 nsVoidArray mFrames;
766 protected:
768 static nsBlinkTimer* sTextBlinker;
769 static PRUint32 sState; // 0-2 == on; 3 == off
773 nsBlinkTimer* nsBlinkTimer::sTextBlinker = nsnull;
774 PRUint32 nsBlinkTimer::sState = 0;
776 #ifdef NOISY_BLINK
777 static PRTime gLastTick;
778 #endif
780 nsBlinkTimer::nsBlinkTimer()
784 nsBlinkTimer::~nsBlinkTimer()
786 Stop();
787 sTextBlinker = nsnull;
790 void nsBlinkTimer::Start()
792 nsresult rv;
793 mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
794 if (NS_OK == rv) {
795 mTimer->InitWithCallback(this, 250, nsITimer::TYPE_REPEATING_PRECISE);
799 void nsBlinkTimer::Stop()
801 if (nsnull != mTimer) {
802 mTimer->Cancel();
806 NS_IMPL_ISUPPORTS1(nsBlinkTimer, nsITimerCallback)
808 void nsBlinkTimer::AddFrame(nsIFrame* aFrame) {
809 mFrames.AppendElement(aFrame);
810 if (1 == mFrames.Count()) {
811 Start();
815 PRBool nsBlinkTimer::RemoveFrame(nsIFrame* aFrame) {
816 PRBool rv = mFrames.RemoveElement(aFrame);
817 if (0 == mFrames.Count()) {
818 Stop();
820 return rv;
823 PRInt32 nsBlinkTimer::FrameCount() {
824 return mFrames.Count();
827 NS_IMETHODIMP nsBlinkTimer::Notify(nsITimer *timer)
829 // Toggle blink state bit so that text code knows whether or not to
830 // render. All text code shares the same flag so that they all blink
831 // in unison.
832 sState = (sState + 1) % 4;
833 if (sState == 1 || sState == 2)
834 // States 0, 1, and 2 are all the same.
835 return NS_OK;
837 #ifdef NOISY_BLINK
838 PRTime now = PR_Now();
839 char buf[50];
840 PRTime delta;
841 LL_SUB(delta, now, gLastTick);
842 gLastTick = now;
843 PR_snprintf(buf, sizeof(buf), "%lldusec", delta);
844 printf("%s\n", buf);
845 #endif
847 PRInt32 i, n = mFrames.Count();
848 for (i = 0; i < n; i++) {
849 nsIFrame* frame = (nsIFrame*) mFrames.ElementAt(i);
851 // Determine damaged area and tell view manager to redraw it
852 // blink doesn't blink outline ... I hope
853 nsRect bounds(nsPoint(0, 0), frame->GetSize());
854 frame->Invalidate(bounds, PR_FALSE);
856 return NS_OK;
860 // static
861 nsresult nsBlinkTimer::AddBlinkFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
863 if (!sTextBlinker)
865 sTextBlinker = new nsBlinkTimer;
866 if (!sTextBlinker) return NS_ERROR_OUT_OF_MEMORY;
869 NS_ADDREF(sTextBlinker);
871 sTextBlinker->AddFrame(aFrame);
872 return NS_OK;
876 // static
877 nsresult nsBlinkTimer::RemoveBlinkFrame(nsIFrame* aFrame)
879 NS_ASSERTION(sTextBlinker, "Should have blink timer here");
881 nsBlinkTimer* blinkTimer = sTextBlinker; // copy so we can call NS_RELEASE on it
882 if (!blinkTimer) return NS_OK;
884 blinkTimer->RemoveFrame(aFrame);
885 NS_RELEASE(blinkTimer);
887 return NS_OK;
890 //----------------------------------------------------------------------
892 nsTextStyle::nsTextStyle(nsPresContext* aPresContext,
893 nsIRenderingContext& aRenderingContext,
894 nsStyleContext* sc)
896 // Get style data
897 mFont = sc->GetStyleFont();
898 mText = sc->GetStyleText();
900 // Cache the original decorations and reuse the current font
901 // to query metrics, rather than creating a new font which is expensive.
902 nsFont* plainFont = (nsFont *)&mFont->mFont; //XXX: Change to use a CONST_CAST macro.
903 NS_ASSERTION(plainFont, "null plainFont: font problems in nsTextStyle::nsTextStyle");
904 PRUint8 originalDecorations = plainFont->decorations;
905 plainFont->decorations = NS_FONT_DECORATION_NONE;
906 mAveCharWidth = 0;
907 // Set the font: some users of the struct expect this state
908 nsLayoutUtils::SetFontFromStyle(&aRenderingContext, sc);
909 aRenderingContext.GetFontMetrics(mNormalFont);
910 mNormalFont->GetSpaceWidth(mSpaceWidth);
911 mNormalFont->GetAveCharWidth(mAveCharWidth);
912 mLastFont = mNormalFont;
914 // Get the small-caps font if needed
915 mSmallCaps = NS_STYLE_FONT_VARIANT_SMALL_CAPS == plainFont->variant;
916 if (mSmallCaps) {
917 nscoord originalSize = plainFont->size;
918 plainFont->size = nscoord(0.8 * plainFont->size);
919 mSmallFont = aPresContext->GetMetricsFor(*plainFont).get(); // addrefs
920 // Reset to the size value saved earlier.
921 plainFont->size = originalSize;
923 else {
924 mSmallFont = nsnull;
927 // Reset to the decoration saved earlier
928 plainFont->decorations = originalDecorations;
930 // Get the word and letter spacing
931 PRIntn unit = mText->mWordSpacing.GetUnit();
932 if (eStyleUnit_Coord == unit) {
933 mWordSpacing = mText->mWordSpacing.GetCoordValue();
934 } else {
935 mWordSpacing = 0;
938 unit = mText->mLetterSpacing.GetUnit();
939 if (eStyleUnit_Coord == unit) {
940 mLetterSpacing = mText->mLetterSpacing.GetCoordValue();
941 } else {
942 mLetterSpacing = 0;
945 mNumJustifiableCharacterToRender = 0;
946 mNumJustifiableCharacterToMeasure = 0;
947 mNumJustifiableCharacterReceivingExtraJot = 0;
948 mExtraSpacePerJustifiableCharacter = 0;
949 mPreformatted = (NS_STYLE_WHITESPACE_PRE == mText->mWhiteSpace) ||
950 (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == mText->mWhiteSpace);
952 mJustifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == mText->mTextAlign) &&
953 !mPreformatted;
956 nsTextStyle::~nsTextStyle() {
957 NS_IF_RELEASE(mNormalFont);
958 NS_IF_RELEASE(mSmallFont);
961 //----------------------------------------------------------------------
963 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB)
965 if (colorA == colorB) {
966 nscolor res;
967 res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
968 NS_GET_G(colorA) ^ 0xff,
969 NS_GET_B(colorA) ^ 0xff);
970 return res;
972 return colorA;
975 //-----------------------------------------------------------------------------
977 nsTextPaintStyle::nsTextPaintStyle(nsPresContext* aPresContext,
978 nsIRenderingContext& aRenderingContext,
979 nsStyleContext* aStyleContext,
980 nsIContent* aContent,
981 PRInt16 aSelectionStatus)
982 : nsTextStyle(aPresContext, aRenderingContext, aStyleContext),
983 mPresContext(nsnull),
984 mStyleContext(nsnull),
985 mContent(nsnull),
986 mInitCommonColors(PR_FALSE),
987 mInitSelectionColors(PR_FALSE)
989 mPresContext = aPresContext;
990 mStyleContext = aStyleContext;
991 mContent = aContent;
992 mSelectionStatus = aSelectionStatus;
993 mColor = mStyleContext->GetStyleColor();
994 for (int i = 0; i < 4; i++)
995 mIMEColor[i].mInit = PR_FALSE;
996 mIMEUnderlineRelativeSize = -1.0f;
999 nsTextPaintStyle::~nsTextPaintStyle()
1001 mColor = nsnull;
1004 PRBool
1005 nsTextPaintStyle::EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor)
1008 if (!aForeColor || !aBackColor)
1009 return PR_FALSE;
1011 // If common colors are not initialized, mFrameBackgroundColor and
1012 // mSufficientContrast are not initialized.
1013 if (!mInitCommonColors && !InitCommonColors())
1014 return PR_FALSE;
1016 // If the combination of selection background color and frame background color
1017 // is sufficient contrast, don't exchange the selection colors.
1018 PRInt32 backLuminosityDifference =
1019 NS_LUMINOSITY_DIFFERENCE(*aBackColor, mFrameBackgroundColor);
1020 if (backLuminosityDifference >= mSufficientContrast)
1021 return PR_FALSE;
1023 // Otherwise, we should use the higher-contrast color for the selection
1024 // background color.
1025 PRInt32 foreLuminosityDifference =
1026 NS_LUMINOSITY_DIFFERENCE(*aForeColor, mFrameBackgroundColor);
1027 if (backLuminosityDifference < foreLuminosityDifference) {
1028 nscolor tmpColor = *aForeColor;
1029 *aForeColor = *aBackColor;
1030 *aBackColor = tmpColor;
1031 return PR_TRUE;
1033 return PR_FALSE;
1036 nscolor
1037 nsTextPaintStyle::GetTextColor()
1039 return mColor->mColor;
1042 void
1043 nsTextPaintStyle::GetSelectionColors(nscolor* aForeColor,
1044 nscolor* aBackColor,
1045 PRBool* aBackIsTransparent)
1047 NS_ASSERTION(aForeColor, "aForeColor is null");
1048 NS_ASSERTION(aBackColor, "aBackColor is null");
1049 NS_ASSERTION(aBackIsTransparent, "aBackIsTransparent is null");
1051 if (!mInitSelectionColors && !InitSelectionColors()) {
1052 NS_ERROR("Fail to initialize selection colors");
1053 return;
1056 *aForeColor = mSelectionTextColor;
1057 *aBackColor = mSelectionBGColor;
1058 *aBackIsTransparent = mSelectionBGIsTransparent;
1061 void
1062 nsTextPaintStyle::GetIMESelectionColors(SelectionType aSelectionType,
1063 nscolor* aForeColor,
1064 nscolor* aBackColor,
1065 PRBool* aBackIsTransparent)
1067 NS_ASSERTION(aForeColor, "aForeColor is null");
1068 NS_ASSERTION(aBackColor, "aBackColor is null");
1069 NS_ASSERTION(aBackIsTransparent, "aBackIsTransparent is null");
1071 nsIMEColor* IMEColor = GetIMEColor(aSelectionType);
1072 if (!IMEColor) {
1073 NS_ERROR("aSelectionType is invalid");
1074 return;
1076 if (!IMEColor->mInit)
1077 return;
1078 *aForeColor = IMEColor->mTextColor;
1079 *aBackColor = IMEColor->mBGColor;
1080 *aBackIsTransparent = IMEColor->mBGIsTransparent;
1083 PRBool
1084 nsTextPaintStyle::GetIMEUnderline(SelectionType aSelectionType,
1085 nscolor* aLineColor,
1086 float* aRelativeSize)
1088 NS_ASSERTION(aLineColor, "aLineColor is null");
1089 NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
1091 nsIMEColor* IMEColor = GetIMEColor(aSelectionType);
1092 if (!IMEColor) {
1093 NS_ERROR("aSelectionType is invalid");
1094 return PR_FALSE;
1096 if (!IMEColor->mInit)
1097 return PR_FALSE;
1098 if (IMEColor->mUnderlineColor == NS_TRANSPARENT ||
1099 mIMEUnderlineRelativeSize <= 0.0f)
1100 return PR_FALSE;
1102 *aLineColor = IMEColor->mUnderlineColor;
1103 *aRelativeSize = mIMEUnderlineRelativeSize;
1104 return PR_TRUE;
1107 PRBool
1108 nsTextPaintStyle::InitCommonColors()
1110 if (!mPresContext || !mStyleContext)
1111 return PR_FALSE;
1113 if (mInitCommonColors)
1114 return PR_TRUE;
1116 const nsStyleBackground* bg =
1117 nsCSSRendering::FindNonTransparentBackground(mStyleContext);
1118 NS_ASSERTION(bg, "Cannot find NonTransparentBackground.");
1119 mFrameBackgroundColor = bg->mBackgroundColor;
1121 nsILookAndFeel* look = mPresContext->LookAndFeel();
1122 if (!look)
1123 return PR_FALSE;
1125 nscolor defaultWindowBackgroundColor, selectionTextColor, selectionBGColor;
1126 look->GetColor(nsILookAndFeel::eColor_TextSelectBackground,
1127 selectionBGColor);
1128 look->GetColor(nsILookAndFeel::eColor_TextSelectForeground,
1129 selectionTextColor);
1130 look->GetColor(nsILookAndFeel::eColor_WindowBackground,
1131 defaultWindowBackgroundColor);
1133 mSufficientContrast =
1134 PR_MIN(PR_MIN(NS_SUFFICIENT_LUMINOSITY_DIFFERENCE,
1135 NS_LUMINOSITY_DIFFERENCE(selectionTextColor,
1136 selectionBGColor)),
1137 NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor,
1138 selectionBGColor));
1140 mInitCommonColors = PR_TRUE;
1141 return PR_TRUE;
1144 PRBool
1145 nsTextPaintStyle::InitSelectionColors()
1147 if (!mPresContext || !mStyleContext)
1148 return PR_FALSE;
1149 if (mInitSelectionColors)
1150 return PR_TRUE;
1152 mSelectionBGIsTransparent = PR_FALSE;
1154 if (mContent &&
1155 mSelectionStatus == nsISelectionController::SELECTION_ON) {
1156 nsRefPtr<nsStyleContext> sc = nsnull;
1157 sc = mPresContext->StyleSet()->
1158 ProbePseudoStyleFor(mContent->GetParent(),
1159 nsCSSPseudoElements::mozSelection, mStyleContext);
1160 // Use -moz-selection pseudo class.
1161 if (sc) {
1162 const nsStyleBackground* bg = sc->GetStyleBackground();
1163 mSelectionBGIsTransparent =
1164 PRBool(bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT);
1165 if (!mSelectionBGIsTransparent)
1166 mSelectionBGColor = bg->mBackgroundColor;
1167 mSelectionTextColor = sc->GetStyleColor()->mColor;
1168 return PR_TRUE;
1172 nsILookAndFeel* look = mPresContext->LookAndFeel();
1173 if (!look)
1174 return PR_FALSE;
1176 nscolor selectionBGColor;
1177 look->GetColor(nsILookAndFeel::eColor_TextSelectBackground,
1178 selectionBGColor);
1180 if (mSelectionStatus == nsISelectionController::SELECTION_ATTENTION) {
1181 look->GetColor(nsILookAndFeel::eColor_TextSelectBackgroundAttention,
1182 mSelectionBGColor);
1183 mSelectionBGColor = EnsureDifferentColors(mSelectionBGColor,
1184 selectionBGColor);
1185 } else if (mSelectionStatus != nsISelectionController::SELECTION_ON) {
1186 look->GetColor(nsILookAndFeel::eColor_TextSelectBackgroundDisabled,
1187 mSelectionBGColor);
1188 mSelectionBGColor = EnsureDifferentColors(mSelectionBGColor,
1189 selectionBGColor);
1190 } else {
1191 mSelectionBGColor = selectionBGColor;
1194 look->GetColor(nsILookAndFeel::eColor_TextSelectForeground,
1195 mSelectionTextColor);
1197 // On MacOS X, we don't exchange text color and BG color.
1198 if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
1199 mSelectionTextColor = EnsureDifferentColors(mColor->mColor,
1200 mSelectionBGColor);
1201 return PR_TRUE;
1204 EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor);
1206 mInitSelectionColors = PR_TRUE;
1207 return PR_TRUE;
1210 nsTextPaintStyle::nsIMEColor*
1211 nsTextPaintStyle::GetIMEColor(SelectionType aSelectionType)
1213 PRInt32 index;
1214 switch (aSelectionType) {
1215 case nsISelectionController::SELECTION_IME_RAWINPUT:
1216 index = eIndexRawInput;
1217 break;
1218 case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
1219 index = eIndexSelRawText;
1220 break;
1221 case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
1222 index = eIndexConvText;
1223 break;
1224 case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
1225 index = eIndexSelConvText;
1226 break;
1227 default:
1228 NS_ERROR("aSelectionType is Invalid");
1229 return nsnull;
1231 nsIMEColor* IMEColor = &mIMEColor[index];
1232 if (!IMEColor->mInit && !InitIMEColors(aSelectionType, IMEColor))
1233 NS_ERROR("Fail to initialize IME color");
1234 return IMEColor;
1237 PRBool
1238 nsTextPaintStyle::InitIMEColors(SelectionType aSelectionType,
1239 nsIMEColor* aIMEColor)
1241 if (!mPresContext || !aIMEColor)
1242 return PR_FALSE;
1244 NS_ASSERTION(!aIMEColor->mInit, "this is already initialized");
1246 nsILookAndFeel::nsColorID foreColorID, backColorID, lineColorID;
1247 switch (aSelectionType) {
1248 case nsISelectionController::SELECTION_IME_RAWINPUT:
1249 foreColorID = nsILookAndFeel::eColor_IMERawInputForeground;
1250 backColorID = nsILookAndFeel::eColor_IMERawInputBackground;
1251 lineColorID = nsILookAndFeel::eColor_IMERawInputUnderline;
1252 break;
1253 case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
1254 foreColorID = nsILookAndFeel::eColor_IMESelectedRawTextForeground;
1255 backColorID = nsILookAndFeel::eColor_IMESelectedRawTextBackground;
1256 lineColorID = nsILookAndFeel::eColor_IMESelectedRawTextUnderline;
1257 break;
1258 case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
1259 foreColorID = nsILookAndFeel::eColor_IMEConvertedTextForeground;
1260 backColorID = nsILookAndFeel::eColor_IMEConvertedTextBackground;
1261 lineColorID = nsILookAndFeel::eColor_IMEConvertedTextUnderline;
1262 break;
1263 case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
1264 foreColorID = nsILookAndFeel::eColor_IMESelectedConvertedTextForeground;
1265 backColorID = nsILookAndFeel::eColor_IMESelectedConvertedTextBackground;
1266 lineColorID = nsILookAndFeel::eColor_IMESelectedConvertedTextUnderline;
1267 break;
1268 default:
1269 NS_ERROR("aSelectionType is Invalid");
1270 return PR_FALSE;
1273 nsILookAndFeel* look = mPresContext->LookAndFeel();
1274 if (!look)
1275 return PR_FALSE;
1277 nscolor foreColor, backColor, lineColor;
1278 look->GetColor(foreColorID, foreColor);
1279 look->GetColor(backColorID, backColor);
1280 look->GetColor(lineColorID, lineColor);
1282 // Convert special color to actual color
1283 NS_ASSERTION(foreColor != NS_TRANSPARENT,
1284 "foreColor cannot be NS_TRANSPARENT");
1285 NS_ASSERTION(backColor != NS_SAME_AS_FOREGROUND_COLOR,
1286 "backColor cannot be NS_SAME_AS_FOREGROUND_COLOR");
1287 NS_ASSERTION(backColor != NS_40PERCENT_FOREGROUND_COLOR,
1288 "backColor cannot be NS_40PERCENT_FOREGROUND_COLOR");
1290 PRBool backIsTransparent = PR_FALSE;
1291 if (backColor == NS_TRANSPARENT)
1292 backIsTransparent = PR_TRUE;
1294 foreColor = GetResolvedForeColor(foreColor, GetTextColor(), backColor);
1296 if (!backIsTransparent)
1297 EnsureSufficientContrast(&foreColor, &backColor);
1299 lineColor = GetResolvedForeColor(lineColor, foreColor, backColor);
1301 aIMEColor->mTextColor = foreColor;
1302 aIMEColor->mBGColor = backColor;
1303 aIMEColor->mBGIsTransparent = backIsTransparent;
1304 aIMEColor->mUnderlineColor = lineColor;
1305 aIMEColor->mInit = PR_TRUE;
1307 if (mIMEUnderlineRelativeSize == -1.0f) {
1308 look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize,
1309 mIMEUnderlineRelativeSize);
1310 NS_ASSERTION(mIMEUnderlineRelativeSize >= 0.0f,
1311 "underline size must be larger than 0");
1314 return PR_TRUE;
1317 inline nscolor Get40PercentColor(nscolor aForeColor, nscolor aBackColor)
1319 nscolor foreColor = NS_RGBA(NS_GET_R(aForeColor),
1320 NS_GET_G(aForeColor),
1321 NS_GET_B(aForeColor),
1322 (PRUint8)(255 * 0.4f));
1323 return NS_ComposeColors(aBackColor, foreColor);
1326 nscolor
1327 nsTextPaintStyle::GetResolvedForeColor(nscolor aColor,
1328 nscolor aDefaultForeColor,
1329 nscolor aBackColor)
1331 if (aColor == NS_SAME_AS_FOREGROUND_COLOR)
1332 return aDefaultForeColor;
1334 if (aColor != NS_40PERCENT_FOREGROUND_COLOR)
1335 return aColor;
1337 // Get actual background color
1338 nscolor actualBGColor = aBackColor;
1339 if (actualBGColor == NS_TRANSPARENT) {
1340 if (!mInitCommonColors && !InitCommonColors())
1341 return aDefaultForeColor;
1342 actualBGColor = mFrameBackgroundColor;
1344 return Get40PercentColor(aDefaultForeColor, actualBGColor);
1347 //-----------------------------------------------------------------------------
1349 #ifdef ACCESSIBILITY
1350 NS_IMETHODIMP nsTextFrame::GetAccessible(nsIAccessible** aAccessible)
1352 if (!IsEmpty() || GetNextInFlow()) {
1354 nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
1356 if (accService) {
1357 return accService->CreateHTMLTextAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible);
1360 return NS_ERROR_FAILURE;
1362 #endif
1365 //-----------------------------------------------------------------------------
1366 NS_IMETHODIMP
1367 nsTextFrame::Init(nsIContent* aContent,
1368 nsIFrame* aParent,
1369 nsIFrame* aPrevInFlow)
1371 NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT),
1372 "Bogus content!");
1373 nsresult rv = nsFrame::Init(aContent, aParent, aPrevInFlow);
1374 if (NS_SUCCEEDED(rv) && !aPrevInFlow &&
1375 GetStyleText()->WhiteSpaceIsSignificant()) {
1376 // We care about our actual length in this case, so we can report the right
1377 // thing from HasTerminalNewline(). Since we're not a continuing frame, we
1378 // should map the whole content node.
1380 // Note that if we're created due to bidi splitting the bidi code
1381 // will override what we compute here, so it's ok.
1382 mContentLength = mContent->TextLength();
1384 return rv;
1387 void
1388 nsTextFrame::Destroy()
1390 if (mNextContinuation) {
1391 mNextContinuation->SetPrevInFlow(nsnull);
1393 // Let the base class destroy the frame
1394 nsFrame::Destroy();
1397 class nsContinuingTextFrame : public nsTextFrame {
1398 public:
1399 friend nsIFrame* NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
1401 NS_IMETHOD Init(nsIContent* aContent,
1402 nsIFrame* aParent,
1403 nsIFrame* aPrevInFlow);
1405 virtual void Destroy();
1407 virtual nsIFrame* GetPrevContinuation() const {
1408 return mPrevContinuation;
1410 NS_IMETHOD SetPrevContinuation(nsIFrame* aPrevContinuation) {
1411 NS_ASSERTION (!aPrevContinuation || GetType() == aPrevContinuation->GetType(),
1412 "setting a prev continuation with incorrect type!");
1413 NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevContinuation, this),
1414 "creating a loop in continuation chain!");
1415 mPrevContinuation = aPrevContinuation;
1416 RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
1417 return NS_OK;
1419 virtual nsIFrame* GetPrevInFlowVirtual() const { return GetPrevInFlow(); }
1420 nsIFrame* GetPrevInFlow() const {
1421 return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nsnull;
1423 NS_IMETHOD SetPrevInFlow(nsIFrame* aPrevInFlow) {
1424 NS_ASSERTION (!aPrevInFlow || GetType() == aPrevInFlow->GetType(),
1425 "setting a prev in flow with incorrect type!");
1426 NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevInFlow, this),
1427 "creating a loop in continuation chain!");
1428 mPrevContinuation = aPrevInFlow;
1429 AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
1430 return NS_OK;
1432 virtual nsIFrame* GetFirstInFlow() const;
1433 virtual nsIFrame* GetFirstContinuation() const;
1435 virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
1436 InlineMinWidthData *aData);
1437 virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
1438 InlinePrefWidthData *aData);
1440 protected:
1441 nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {}
1442 nsIFrame* mPrevContinuation;
1445 NS_IMETHODIMP
1446 nsContinuingTextFrame::Init(nsIContent* aContent,
1447 nsIFrame* aParent,
1448 nsIFrame* aPrevInFlow)
1450 nsresult rv = nsTextFrame::Init(aContent, aParent, aPrevInFlow);
1452 if (aPrevInFlow) {
1453 nsIFrame* nextContinuation = aPrevInFlow->GetNextContinuation();
1454 // Hook the frame into the flow
1455 SetPrevInFlow(aPrevInFlow);
1456 aPrevInFlow->SetNextInFlow(this);
1457 #ifdef IBMBIDI
1458 if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
1459 PRInt32 start, end;
1460 aPrevInFlow->GetOffsets(start, mContentOffset);
1462 nsPropertyTable *propTable = PresContext()->PropertyTable();
1463 propTable->SetProperty(this, nsGkAtoms::embeddingLevel,
1464 propTable->GetProperty(aPrevInFlow, nsGkAtoms::embeddingLevel),
1465 nsnull, nsnull);
1466 propTable->SetProperty(this, nsGkAtoms::baseLevel,
1467 propTable->GetProperty(aPrevInFlow, nsGkAtoms::baseLevel),
1468 nsnull, nsnull);
1469 propTable->SetProperty(this, nsGkAtoms::charType,
1470 propTable->GetProperty(aPrevInFlow, nsGkAtoms::charType),
1471 nsnull, nsnull);
1472 if (nextContinuation) {
1473 SetNextContinuation(nextContinuation);
1474 nextContinuation->SetPrevContinuation(this);
1475 nextContinuation->GetOffsets(start, end);
1476 mContentLength = PR_MAX(1, start - mContentOffset);
1478 mState |= NS_FRAME_IS_BIDI;
1479 } // prev frame is bidi
1480 #endif // IBMBIDI
1483 return rv;
1486 void
1487 nsContinuingTextFrame::Destroy()
1489 if (mPrevContinuation || mNextContinuation) {
1490 nsSplittableFrame::RemoveFromFlow(this);
1492 // Let the base class destroy the frame
1493 nsFrame::Destroy();
1496 nsIFrame*
1497 nsContinuingTextFrame::GetFirstInFlow() const
1499 // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
1500 nsIFrame *firstInFlow,
1501 *previous = NS_CONST_CAST(nsIFrame*,
1502 NS_STATIC_CAST(const nsIFrame*, this));
1503 do {
1504 firstInFlow = previous;
1505 previous = firstInFlow->GetPrevInFlow();
1506 } while (previous);
1507 return firstInFlow;
1510 nsIFrame*
1511 nsContinuingTextFrame::GetFirstContinuation() const
1513 // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
1514 nsIFrame *firstContinuation,
1515 *previous = NS_CONST_CAST(nsIFrame*,
1516 NS_STATIC_CAST(const nsIFrame*, mPrevContinuation));
1517 do {
1518 firstContinuation = previous;
1519 previous = firstContinuation->GetPrevContinuation();
1520 } while (previous);
1521 return firstContinuation;
1524 // XXX Do we want to do all the work for the first-in-flow or do the
1525 // work for each part? (Be careful of first-letter / first-line, though,
1526 // especially first-line!) Doing all the work on the first-in-flow has
1527 // the advantage of avoiding the potential for incremental reflow bugs,
1528 // but depends on our maintining the frame tree in reasonable ways even
1529 // for edge cases (block-within-inline splits, nextBidi, etc.)
1531 // XXX We really need to make :first-letter happen during frame
1532 // construction.
1534 // Needed for text frames in XUL.
1535 /* virtual */ nscoord
1536 nsTextFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
1538 return nsLayoutUtils::MinWidthFromInline(this, aRenderingContext);
1541 // Needed for text frames in XUL.
1542 /* virtual */ nscoord
1543 nsTextFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
1545 return nsLayoutUtils::PrefWidthFromInline(this, aRenderingContext);
1548 /* virtual */ void
1549 nsContinuingTextFrame::AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
1550 InlineMinWidthData *aData)
1552 // Do nothing, since the first-in-flow accounts for everything.
1553 return;
1556 /* virtual */ void
1557 nsContinuingTextFrame::AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
1558 InlinePrefWidthData *aData)
1560 // Do nothing, since the first-in-flow accounts for everything.
1561 return;
1564 //DRAW SELECTION ITERATOR USED FOR TEXTFRAMES ONLY
1565 //helper class for drawing multiply selected text
1566 class DrawSelectionIterator
1568 public:
1569 DrawSelectionIterator(const SelectionDetails *aSelDetails, PRUnichar *aText,
1570 PRUint32 aTextLength, nsTextPaintStyle *aTextStyle,
1571 SelectionType aCareSelections);
1572 ~DrawSelectionIterator();
1573 PRBool First();
1574 PRBool Next();
1575 PRBool IsDone();
1576 PRBool IsLast();
1578 PRUnichar * CurrentTextUnicharPtr();
1579 char * CurrentTextCStrPtr();
1580 PRUint32 CurrentLength();
1581 PRBool IsBeforeOrAfter();
1584 * Get foreground color, background color, whether the background is transparent,
1585 * and whether the current range is the normal selection.
1587 * @param aForeColor [out] returns the foreground color of the current range.
1588 * @param aBackColor [out] returns the background color of the current range.
1589 * Note that this value is undefined if aBackIsTransparent
1590 * is true or if @return is false.
1591 * @param aBackIsTransparent [out] returns whether the background is transparent.
1592 * If true, the background is transparent.
1593 * Otherwise, it isn't so.
1594 * @return whether the current range is a normal selection.
1596 PRBool GetSelectionColors(nscolor *aForeColor, nscolor *aBackColor, PRBool *aBackIsTransparent);
1597 private:
1598 union {
1599 PRUnichar *mUniStr;
1600 char *mCStr;
1602 PRUint32 mLength;
1603 PRUint32 mCurrentIdx;
1604 PRUint32 mCurrentLength;
1605 nsTextPaintStyle* mOldStyle;//base new styles on this one???
1606 const SelectionDetails *mDetails;
1607 PRBool mDone;
1608 PRUint8 * mTypes;
1609 PRBool mInit;
1610 //private methods
1611 void FillCurrentData();
1614 DrawSelectionIterator::DrawSelectionIterator(const SelectionDetails *aSelDetails,
1615 PRUnichar *aText,
1616 PRUint32 aTextLength,
1617 nsTextPaintStyle* aTextStyle,
1618 SelectionType aCareSelections)
1619 :mOldStyle(aTextStyle)
1621 NS_ASSERTION(aCareSelections, "aCareSelection value must not be zero!");
1623 mDetails = aSelDetails;
1624 mCurrentIdx = 0;
1625 mUniStr = aText;
1626 mLength = aTextLength;
1627 mTypes = nsnull;
1628 mInit = PR_FALSE;
1630 if (!aSelDetails) {
1631 mDone = PR_TRUE;
1632 return;
1634 mDone = (PRBool)(mCurrentIdx>=mLength);
1635 if (mDone)
1636 return;
1638 //special case for 1 selection. later
1639 const SelectionDetails *details = aSelDetails;
1640 if (details->mNext) {
1641 // go to next
1642 } else if (details->mStart == details->mEnd) {
1643 // no collapsed selections here!
1644 mDone = PR_TRUE;
1645 return;
1646 } else if (!(details->mType & aCareSelections)) {
1647 //if all we have is selection we DONT care about, do nothing
1648 mDone = PR_TRUE;
1649 return;
1652 mTypes = new PRUint8[mLength];
1653 if (!mTypes)
1654 return;
1655 memset(mTypes, 0, mLength);
1656 while (details) {
1657 if ((details->mType & aCareSelections) &&
1658 (details->mStart != details->mEnd)) {
1659 mInit = PR_TRUE; // WE FOUND SOMETHING WE CARE ABOUT
1660 for (int i = details->mStart; i < details->mEnd; i++) {
1661 if ((PRUint32)i >= mLength) {
1662 NS_ASSERTION(0, "Selection Details out of range?");
1663 return;
1665 mTypes[i] |= details->mType;
1668 details= details->mNext;
1670 if (!mInit) {
1671 // we have details but none that we care about.
1672 delete [] mTypes;
1673 mTypes = nsnull;
1674 mDone = PR_TRUE; // we are finished
1675 mInit = PR_TRUE;
1679 DrawSelectionIterator::~DrawSelectionIterator()
1681 if (mTypes)
1682 delete [] mTypes;
1685 void
1686 DrawSelectionIterator::FillCurrentData()
1688 if (mDone)
1689 return;
1690 mCurrentIdx += mCurrentLength; // advance to this chunk
1691 mCurrentLength = 0;
1692 if (mCurrentIdx >= mLength)
1694 mDone = PR_TRUE;
1695 return;
1697 if (!mTypes)
1699 if (mCurrentIdx < (PRUint32)mDetails->mStart)
1701 mCurrentLength = mDetails->mStart;
1703 else if (mCurrentIdx == (PRUint32)mDetails->mStart)
1704 {//start
1705 mCurrentLength = mDetails->mEnd-mCurrentIdx;
1707 else if (mCurrentIdx > (PRUint32)mDetails->mStart)//last unselected part
1709 mCurrentLength = mLength - mDetails->mEnd;
1712 else
1714 uint8 typevalue = mTypes[mCurrentIdx];
1715 while (mCurrentIdx+mCurrentLength < mLength && typevalue == mTypes[mCurrentIdx+mCurrentLength])
1717 mCurrentLength++;
1720 // never overrun past mLength
1721 if (mCurrentIdx+mCurrentLength > mLength)
1723 mCurrentLength = mLength - mCurrentIdx;
1727 PRBool
1728 DrawSelectionIterator::First()
1730 if (!mInit)
1731 return PR_FALSE;
1732 mCurrentIdx = 0;
1733 mCurrentLength = 0;
1734 if (!mTypes && mDetails->mStart == mDetails->mEnd)//no collapsed selections here!
1735 mDone = PR_TRUE;
1736 mDone = (mCurrentIdx+mCurrentLength) >= mLength;
1737 FillCurrentData();
1738 return PR_TRUE;
1743 PRBool
1744 DrawSelectionIterator::Next()
1746 if (mDone || !mInit)
1747 return PR_FALSE;
1748 FillCurrentData();//advances to next chunk
1749 return PR_TRUE;
1752 PRBool
1753 DrawSelectionIterator::IsLast()
1755 return mDone || !mInit || mCurrentIdx + mCurrentLength >= mLength;
1758 PRBool
1759 DrawSelectionIterator::IsDone()
1761 return mDone || !mInit;
1765 PRUnichar *
1766 DrawSelectionIterator::CurrentTextUnicharPtr()
1768 return mUniStr+mCurrentIdx;
1771 char *
1772 DrawSelectionIterator::CurrentTextCStrPtr()
1774 return mCStr+mCurrentIdx;
1777 PRUint32
1778 DrawSelectionIterator::CurrentLength()
1780 return mCurrentLength;
1783 PRBool
1784 DrawSelectionIterator::GetSelectionColors(nscolor *aForeColor,
1785 nscolor *aBackColor,
1786 PRBool *aBackIsTransparent)
1788 if (mTypes) {
1789 // Normal selection
1790 if (mTypes[mCurrentIdx] & nsTextPaintStyle::eNormalSelection) {
1791 mOldStyle->GetSelectionColors(aForeColor, aBackColor,
1792 aBackIsTransparent);
1793 return PR_TRUE;
1796 // IME selections
1797 if (mTypes[mCurrentIdx] & nsTextPaintStyle::eIMESelections) {
1798 mOldStyle->GetIMESelectionColors(mTypes[mCurrentIdx],
1799 aForeColor, aBackColor,
1800 aBackIsTransparent);
1801 return PR_TRUE;
1805 // Non-supported Selection or Non-selection text
1806 *aBackIsTransparent = PR_FALSE;
1807 *aForeColor = mOldStyle->GetTextColor();
1808 return PR_FALSE;
1811 PRBool
1812 DrawSelectionIterator::IsBeforeOrAfter()
1814 return mCurrentIdx != (PRUint32)mDetails->mStart;
1817 //END DRAWSELECTIONITERATOR!!
1822 // Flag information used by rendering code. This information is
1823 // computed by the ResizeReflow code. The flags are stored in the
1824 // mState variable in the frame class private section.
1826 // Flag indicating that whitespace was skipped
1827 #define TEXT_SKIP_LEADING_WS 0x01000000
1828 #define TEXT_HAS_MULTIBYTE 0x02000000
1829 #define TEXT_IN_WORD 0x04000000
1830 // This bit is set on the first frame in a continuation indicating
1831 // that it was chopped short because of :first-letter style.
1832 #define TEXT_FIRST_LETTER 0x08000000
1833 #define TEXT_WAS_TRANSFORMED 0x10000000
1835 // Bits in mState used for reflow flags
1836 #define TEXT_REFLOW_FLAGS 0x1F000000
1838 #define TEXT_TRIMMED_WS 0x20000000
1840 #define TEXT_OPTIMIZE_RESIZE 0x40000000
1842 #define TEXT_BLINK_ON 0x80000000
1844 #define TEXT_IS_ONLY_WHITESPACE 0x00100000
1846 #define TEXT_ISNOT_ONLY_WHITESPACE 0x00200000
1848 #define TEXT_WHITESPACE_FLAGS 0x00300000
1850 #define TEXT_IS_END_OF_LINE 0x00400000
1852 //----------------------------------------------------------------------
1854 #if defined(DEBUG_rbs) || defined(DEBUG_bzbarsky)
1855 static void
1856 VerifyNotDirty(nsFrameState state)
1858 PRBool isZero = state & NS_FRAME_FIRST_REFLOW;
1859 PRBool isDirty = state & NS_FRAME_IS_DIRTY;
1860 if (!isZero && isDirty)
1861 NS_WARNING("internal offsets may be out-of-sync");
1863 #define DEBUG_VERIFY_NOT_DIRTY(state) \
1864 VerifyNotDirty(state)
1865 #else
1866 #define DEBUG_VERIFY_NOT_DIRTY(state)
1867 #endif
1869 nsIFrame*
1870 NS_NewTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1872 return new (aPresShell) nsTextFrame(aContext);
1875 nsIFrame*
1876 NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1878 return new (aPresShell) nsContinuingTextFrame(aContext);
1881 nsTextFrame::~nsTextFrame()
1883 if (0 != (mState & TEXT_BLINK_ON))
1885 nsBlinkTimer::RemoveBlinkFrame(this);
1889 nsIDocument*
1890 nsTextFrame::GetDocument(nsPresContext* aPresContext)
1892 nsIDocument *result = nsnull;
1893 if (mContent) {
1894 result = mContent->GetDocument();
1896 if (!result && aPresContext) {
1897 result = aPresContext->PresShell()->GetDocument();
1899 return result;
1902 NS_IMETHODIMP
1903 nsTextFrame::GetCursor(const nsPoint& aPoint,
1904 nsIFrame::Cursor& aCursor)
1906 FillCursorInformationFromStyle(GetStyleUserInterface(), aCursor);
1907 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
1908 aCursor.mCursor = NS_STYLE_CURSOR_TEXT;
1910 // If tabindex >= 0, use default cursor to indicate it's not selectable
1911 nsIFrame *ancestorFrame = this;
1912 while ((ancestorFrame = ancestorFrame->GetParent()) != nsnull) {
1913 nsIContent *ancestorContent = ancestorFrame->GetContent();
1914 if (ancestorContent && ancestorContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
1915 nsAutoString tabIndexStr;
1916 ancestorContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
1917 if (!tabIndexStr.IsEmpty()) {
1918 PRInt32 rv, tabIndexVal = tabIndexStr.ToInteger(&rv);
1919 if (NS_SUCCEEDED(rv) && tabIndexVal >= 0) {
1920 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
1921 break;
1928 return NS_OK;
1931 nsIFrame*
1932 nsTextFrame::GetLastInFlow() const
1934 nsTextFrame* lastInFlow = NS_CONST_CAST(nsTextFrame*, this);
1935 while (lastInFlow->GetNextInFlow()) {
1936 lastInFlow = NS_STATIC_CAST(nsTextFrame*, lastInFlow->GetNextInFlow());
1938 NS_POSTCONDITION(lastInFlow, "illegal state in flow chain.");
1939 return lastInFlow;
1941 nsIFrame*
1942 nsTextFrame::GetLastContinuation() const
1944 nsTextFrame* lastInFlow = NS_CONST_CAST(nsTextFrame*, this);
1945 while (lastInFlow->mNextContinuation) {
1946 lastInFlow = NS_STATIC_CAST(nsTextFrame*, lastInFlow->mNextContinuation);
1948 NS_POSTCONDITION(lastInFlow, "illegal state in continuation chain.");
1949 return lastInFlow;
1953 NS_IMETHODIMP
1954 nsTextFrame::CharacterDataChanged(nsPresContext* aPresContext,
1955 nsIContent* aChild,
1956 PRBool aAppend)
1958 nsIFrame* targetTextFrame = this;
1960 if (aAppend) {
1961 nsTextFrame* frame = NS_STATIC_CAST(nsTextFrame*, GetLastContinuation());
1962 frame->mState &= ~TEXT_WHITESPACE_FLAGS;
1963 targetTextFrame = frame;
1964 } else {
1965 // Mark this frame and all the next-in-flow frames as dirty and reset all
1966 // the content offsets and lengths to 0, since they no longer know what
1967 // content is ok to access.
1969 // Don't set NS_FRAME_IS_DIRTY on |this|, since we call FrameNeedsReflow
1970 // below.
1971 nsTextFrame* textFrame = this;
1972 do {
1973 textFrame->mState &= ~TEXT_WHITESPACE_FLAGS;
1974 textFrame->mContentOffset = 0;
1975 textFrame->mContentLength = 0;
1976 textFrame = NS_STATIC_CAST(nsTextFrame*, textFrame->GetNextContinuation());
1977 if (!textFrame) {
1978 break;
1980 textFrame->mState |= NS_FRAME_IS_DIRTY;
1981 } while (1);
1984 // Ask the parent frame to reflow me.
1985 aPresContext->GetPresShell()->FrameNeedsReflow(targetTextFrame,
1986 nsIPresShell::eStyleChange,
1987 NS_FRAME_IS_DIRTY);
1989 return NS_OK;
1992 // When we fix nsTextFrame to handle bearing (character glyphs that
1993 // extend outside the frame) by giving it overflow area, we'll need to fix
1994 // this to use the overflow area as its bounds.
1995 class nsDisplayText : public nsDisplayItem {
1996 public:
1997 nsDisplayText(nsTextFrame* aFrame) : nsDisplayItem(aFrame) {
1998 MOZ_COUNT_CTOR(nsDisplayText);
2000 #ifdef NS_BUILD_REFCNT_LOGGING
2001 virtual ~nsDisplayText() {
2002 MOZ_COUNT_DTOR(nsDisplayText);
2004 #endif
2006 virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt) { return mFrame; }
2007 virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
2008 const nsRect& aDirtyRect);
2009 NS_DISPLAY_DECL_NAME("Text")
2012 void
2013 nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
2014 nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
2015 NS_STATIC_CAST(nsTextFrame*, mFrame)->
2016 PaintText(*aCtx, aBuilder->ToReferenceFrame(mFrame));
2019 NS_IMETHODIMP
2020 nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
2021 const nsRect& aDirtyRect,
2022 const nsDisplayListSet& aLists)
2024 if (!IsVisibleForPainting(aBuilder))
2025 return NS_OK;
2027 DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame");
2029 if ((0 != (mState & TEXT_BLINK_ON)) && nsBlinkTimer::GetBlinkIsOff())
2030 return NS_OK;
2032 return aLists.Content()->AppendNewToTop(new (aBuilder) nsDisplayText(this));
2035 void
2036 nsTextFrame::PaintText(nsIRenderingContext& aRenderingContext, nsPoint aPt)
2038 nsStyleContext* sc = mStyleContext;
2039 nsPresContext* presContext = PresContext();
2040 nsCOMPtr<nsIContent> content;
2041 PRInt32 offset, length;
2042 GetContentAndOffsetsForSelection(presContext,
2043 getter_AddRefs(content),
2044 &offset, &length);
2045 PRInt16 selectionValue;
2046 if (NS_FAILED(GetSelectionStatus(presContext, selectionValue)))
2047 selectionValue = nsISelectionController::SELECTION_NORMAL;
2049 nsTextPaintStyle ts(presContext, aRenderingContext, mStyleContext, content,
2050 selectionValue);
2051 SetupTextRunDirection(presContext, &aRenderingContext);
2052 if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing)
2053 || ts.mJustifying) {
2054 PaintTextSlowly(presContext, aRenderingContext, sc, ts, aPt.x, aPt.y);
2056 else {
2057 // Get the text fragment
2058 const nsTextFragment* frag = mContent->GetText();
2059 if (!frag) {
2060 return;
2063 // Choose rendering pathway based on rendering context performance
2064 // hint, whether it needs to be transformed, and whether it's
2065 // multi-byte
2066 PRBool hasMultiByteChars = (0 != (mState & TEXT_HAS_MULTIBYTE));
2067 PRUint32 hints = 0;
2068 aRenderingContext.GetHints(hints);
2070 #ifdef IBMBIDI
2071 PRBool bidiEnabled = presContext->BidiEnabled();
2072 #else
2073 const PRBool bidiEnabled = PR_FALSE;
2074 #endif // IBMBIDI
2075 // * BiDi text or text with multi-byte characters must always be
2076 // rendered as Unicode.
2077 // * Non-transformed, 1-byte text should always be rendered as
2078 // ASCII.
2079 // * Other transformed or 2-byte text should be rendered according
2080 // to the preference of the hint from the rendering context.
2081 if (bidiEnabled || hasMultiByteChars ||
2082 ((0 == (hints & NS_RENDERING_HINT_FAST_8BIT_TEXT)) &&
2083 (frag->Is2b() || (0 != (mState & TEXT_WAS_TRANSFORMED))))) {
2084 PaintUnicodeText(presContext, aRenderingContext, sc, ts, aPt.x, aPt.y);
2086 else {
2087 PaintAsciiText(presContext, aRenderingContext, sc, ts, aPt.x, aPt.y);
2092 PRBool
2093 nsTextFrame::IsChineseJapaneseLangGroup()
2095 const nsStyleVisibility* visibility = mStyleContext->GetStyleVisibility();
2096 if (visibility->mLangGroup == nsGkAtoms::Japanese
2097 || visibility->mLangGroup == nsGkAtoms::Chinese
2098 || visibility->mLangGroup == nsGkAtoms::Taiwanese
2099 || visibility->mLangGroup == nsGkAtoms::HongKongChinese)
2100 return PR_TRUE;
2102 return PR_FALSE;
2106 * Currently only Unicode characters below 0x10000 have their spacing modified
2107 * by justification. If characters above 0x10000 turn out to need
2108 * justification spacing, that will require extra work. Currently,
2109 * this function must not include 0xd800 to 0xdbff because these characters
2110 * are surrogates.
2112 PRBool
2113 nsTextFrame::IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ)
2115 if (0x20u == aChar || 0xa0u == aChar)
2116 return PR_TRUE;
2117 if (aChar < 0x2150u)
2118 return PR_FALSE;
2119 if (aLangIsCJ && (
2120 (0x2150u <= aChar && aChar <= 0x22ffu) || // Number Forms, Arrows, Mathematical Operators
2121 (0x2460u <= aChar && aChar <= 0x24ffu) || // Enclosed Alphanumerics
2122 (0x2580u <= aChar && aChar <= 0x27bfu) || // Block Elements, Geometric Shapes, Miscellaneous Symbols, Dingbats
2123 (0x27f0u <= aChar && aChar <= 0x2bffu) || // Supplemental Arrows-A, Braille Patterns, Supplemental Arrows-B,
2124 // Miscellaneous Mathematical Symbols-B, Supplemental Mathematical Operators,
2125 // Miscellaneous Symbols and Arrows
2126 (0x2e80u <= aChar && aChar <= 0x312fu) || // CJK Radicals Supplement, CJK Radicals Supplement,
2127 // Ideographic Description Characters, CJK Symbols and Punctuation,
2128 // Hiragana, Katakana, Bopomofo
2129 (0x3190u <= aChar && aChar <= 0xabffu) || // Kanbun, Bopomofo Extended, Katakana Phonetic Extensions,
2130 // Enclosed CJK Letters and Months, CJK Compatibility,
2131 // CJK Unified Ideographs Extension A, Yijing Hexagram Symbols,
2132 // CJK Unified Ideographs, Yi Syllables, Yi Radicals
2133 (0xf900u <= aChar && aChar <= 0xfaffu) || // CJK Compatibility Ideographs
2134 (0xff5eu <= aChar && aChar <= 0xff9fu) // Halfwidth and Fullwidth Forms(a part)
2136 return PR_TRUE;
2137 return PR_FALSE;
2140 nsresult
2141 nsTextFrame::FillClusterBuffer(nsPresContext *aPresContext, const PRUnichar *aText,
2142 PRUint32 aLength, nsAutoPRUint8Buffer& aClusterBuffer)
2144 nsresult rv = aClusterBuffer.GrowTo(aLength);
2145 NS_ENSURE_SUCCESS(rv, rv);
2147 // Fill in the cluster hint information, if it's available.
2148 nsCOMPtr<nsIRenderingContext> acx;
2149 PRUint32 clusterHint = 0;
2151 nsIPresShell *shell = aPresContext->GetPresShell();
2152 if (shell) {
2153 rv = shell->CreateRenderingContext(this, getter_AddRefs(acx));
2154 NS_ENSURE_SUCCESS(rv, rv);
2156 // Find the font metrics for this text
2157 nsLayoutUtils::SetFontFromStyle(acx, mStyleContext);
2159 acx->GetHints(clusterHint);
2160 clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
2163 if (clusterHint) {
2164 rv = acx->GetClusterInfo(aText, aLength, aClusterBuffer.mBuffer);
2166 else {
2167 memset(aClusterBuffer.mBuffer, 1, sizeof(PRInt8) * aLength);
2170 return rv;
2173 inline PRBool IsEndOfLine(nsFrameState aState)
2175 return (aState & TEXT_IS_END_OF_LINE) ? PR_TRUE : PR_FALSE;
2178 void nsTextFrame::SetupTextRunDirection(nsPresContext* aPresContext,
2179 nsIRenderingContext* aContext)
2181 PRBool isRTL = aPresContext->BidiEnabled() && (NS_GET_EMBEDDING_LEVEL(this) & 1);
2182 aContext->SetTextRunRTL(isRTL);
2186 * Prepare the text in the content for rendering. If aIndexes is not nsnull
2187 * then fill in aIndexes's with the mapping from the original input to
2188 * the prepared output.
2190 void
2191 nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX,
2192 nsAutoIndexBuffer* aIndexBuffer,
2193 nsAutoTextBuffer* aTextBuffer,
2194 PRInt32* aTextLen,
2195 PRBool aForceArabicShaping,
2196 PRIntn* aJustifiableCharCount,
2197 PRBool aRemoveMultipleTrimmedWS)
2199 // Setup transform to operate starting in the content at our content
2200 // offset
2201 aTX.Init(this, mContent, mContentOffset, aForceArabicShaping);
2203 PRInt32 strInx = mContentOffset;
2204 PRInt32* indexp = aIndexBuffer ? aIndexBuffer->mBuffer : nsnull;
2206 // Skip over the leading whitespace
2207 PRInt32 n = mContentLength;
2208 if (0 != (mState & TEXT_SKIP_LEADING_WS)) {
2209 PRBool isWhitespace, wasTransformed;
2210 PRInt32 wordLen, contentLen;
2211 // Set maximum word length. This is an ABUSE of the variable
2212 // because on entry, this is DOM content length, but on exit,
2213 // GetNextWord returns a transformed-string length in here!
2214 wordLen = mContentOffset + mContentLength;
2215 aTX.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed);
2216 // we trip this assertion in bug 31053, but I think it's unnecessary
2217 //NS_ASSERTION(isWhitespace, "mState and content are out of sync");
2219 if (isWhitespace) {
2220 if (nsnull != indexp) {
2221 // Point mapping indicies at the same content index since
2222 // all of the compressed whitespace maps down to the same
2223 // renderable character.
2224 PRInt32 i = contentLen;
2225 while (--i >= 0) {
2226 *indexp++ = strInx;
2229 n -= contentLen;
2230 if(n<0)
2231 NS_WARNING("mContentLength is < FragmentLength");
2235 // Rescan the content and transform it. Stop when we have consumed
2236 // mContentLength characters.
2237 PRUint8 textTransform = GetStyleText()->mTextTransform;
2238 PRBool inWord = (TEXT_IN_WORD & mState) ? PR_TRUE : PR_FALSE;
2239 PRInt32 column = mColumn;
2240 PRInt32 textLength = 0;
2241 PRInt32 dstOffset = 0;
2243 nsAutoTextBuffer tmpTextBuffer;
2244 nsAutoTextBuffer* textBuffer = aTextBuffer;
2245 if (!textBuffer && aJustifiableCharCount)
2246 textBuffer = &tmpTextBuffer;
2248 while (n > 0) {
2249 PRUnichar* bp;
2250 PRBool isWhitespace, wasTransformed;
2251 PRInt32 wordLen, contentLen;
2253 // Set maximum word length. This is an ABUSE of the variable
2254 // because on entry, this is DOM content length, but on exit,
2255 // GetNextWord returns a transformed-string length in here!
2256 wordLen = mContentOffset + mContentLength;
2257 // Get the next word
2258 bp = aTX.GetNextWord(inWord, &wordLen, &contentLen, &isWhitespace, &wasTransformed);
2259 if (nsnull == bp) {
2260 if (indexp) {
2261 while (--n >= 0) {
2262 *indexp++ = strInx;
2265 break;
2267 inWord = PR_FALSE;
2268 if (isWhitespace) {
2269 if ('\t' == bp[0]) {
2270 PRInt32 spaces = 8 - (7 & column);
2271 PRUnichar* tp = bp;
2272 wordLen = spaces;
2273 while (--spaces >= 0) {
2274 *tp++ = ' ';
2276 // XXX This is a one to many mapping that I think isn't handled well
2277 if (nsnull != indexp) {
2278 *indexp++ = strInx;
2279 strInx += wordLen;
2282 else if ('\n' == bp[0]) {
2283 if (nsnull != indexp) {
2284 *indexp++ = strInx;
2286 break;
2288 else if (nsnull != indexp) {
2289 if (1 == wordLen) {
2290 // Point mapping indicies at the same content index since
2291 // all of the compressed whitespace maps down to the same
2292 // renderable character.
2293 PRInt32 i = contentLen;
2294 while (--i >= 0) {
2295 *indexp++ = strInx;
2297 strInx++;
2298 } else {
2299 // Point mapping indicies at each content index in the word
2300 PRInt32 i = contentLen;
2301 while (--i >= 0) {
2302 *indexp++ = strInx++;
2307 else {
2308 PRInt32 i = contentLen;
2309 if (nsnull != indexp) {
2310 // Point mapping indices at each content index in the word
2311 if (!wasTransformed) {
2312 while (--i >= 0) {
2313 *indexp++ = strInx++;
2315 } else {
2316 PRUnichar* tp = bp;
2317 PRBool caseChanged =
2318 textTransform == NS_STYLE_TEXT_TRANSFORM_UPPERCASE ||
2319 textTransform == NS_STYLE_TEXT_TRANSFORM_CAPITALIZE;
2320 while (--i >= 0) {
2321 PRUnichar ch = aTX.GetContentCharAt(mContentOffset +
2322 indexp - aIndexBuffer->mBuffer);
2323 if (IS_DISCARDED(ch) || ch == '\n') {
2324 *indexp++ = strInx;
2325 continue;
2327 // Point lam and alef to lamalef in shaped text
2328 if (aTX.NeedsArabicShaping()) {
2329 if (IS_LAM(ch) && IS_LAMALEF(*tp)) {
2330 // No need to check for index > length, since
2331 // GetContentCharAt() checks
2332 PRUnichar ch1 = aTX.GetContentCharAt(mContentOffset +
2333 indexp + 1 - aIndexBuffer->mBuffer);
2334 if (IS_ALEF(ch1)) {
2335 *indexp++ = strInx;
2336 --i;
2340 *indexp++ = strInx++;
2341 // Point any capitalized German &szlig; to 'SS'
2342 if (caseChanged && ch == kSZLIG && *tp == PRUnichar('S')) {
2343 ++strInx;
2344 ++tp;
2346 ++tp;
2352 // Grow the buffer before we run out of room.
2353 if (textBuffer != nsnull && dstOffset + wordLen > textBuffer->mBufferLen) {
2354 nsresult rv = textBuffer->GrowBy(wordLen);
2355 if (NS_FAILED(rv)) {
2356 break;
2360 column += wordLen;
2361 textLength += wordLen;
2362 n -= contentLen;
2363 if (textBuffer != nsnull) {
2364 memcpy(textBuffer->mBuffer + dstOffset, bp,
2365 sizeof(PRUnichar)*wordLen);
2367 dstOffset += wordLen;
2370 #ifdef DEBUG
2371 if (aIndexBuffer) {
2372 NS_ASSERTION(indexp <= aIndexBuffer->mBuffer + aIndexBuffer->mBufferLen,
2373 "yikes - we just overwrote memory");
2375 if (textBuffer) {
2376 NS_ASSERTION(dstOffset <= textBuffer->mBufferLen,
2377 "yikes - we just overwrote memory");
2380 #endif
2382 // Remove trailing whitespace if it was trimmed after reflow
2383 // TEXT_TRIMMED_WS can be set in measureText during reflow, and
2384 // nonexitent text buffer may occur in this situation.
2385 if (TEXT_TRIMMED_WS & mState && textBuffer) {
2386 while (--dstOffset >= 0) {
2387 PRUnichar ch = textBuffer->mBuffer[dstOffset];
2388 if (XP_IS_SPACE(ch))
2389 textLength--;
2390 else
2391 break;
2392 if (!aRemoveMultipleTrimmedWS)
2393 break;
2397 if (aIndexBuffer) {
2398 PRInt32* ip = aIndexBuffer->mBuffer;
2399 // Make sure no indexes point beyond text length
2400 for (PRInt32 i = mContentLength - 1; i >= 0; i--) {
2401 if (ip[i] > textLength + mContentOffset)
2402 ip[i] = textLength + mContentOffset;
2403 else
2404 break;
2406 ip[mContentLength] = ip[mContentLength-1];
2407 if ((ip[mContentLength] - mContentOffset) < textLength) {
2408 // Must set up last one for selection beyond edge if in boundary
2409 ip[mContentLength] = textLength + mContentOffset;
2413 *aTextLen = textLength;
2415 if (aJustifiableCharCount && textBuffer) {
2416 PRBool isCJ = IsChineseJapaneseLangGroup();
2417 PRIntn numJustifiableCharacter = 0;
2418 PRInt32 justifiableRange = textLength;
2419 if (IsEndOfLine(mState))
2420 justifiableRange--;
2421 for (PRInt32 i = 0; i < justifiableRange; i++) {
2422 if (IsJustifiableCharacter(textBuffer->mBuffer[i], isCJ))
2423 numJustifiableCharacter++;
2425 *aJustifiableCharCount = numJustifiableCharacter;
2430 //#define SHOW_SELECTION_CURSOR // should be turned off when the caret code is activated
2432 #ifdef SHOW_SELECTION_CURSOR
2434 // XXX This clearly needs to be done by the container, *somehow*
2435 #define CURSOR_COLOR NS_RGB(0,0,255)
2436 static void
2437 RenderSelectionCursor(nsIRenderingContext& aRenderingContext,
2438 nscoord dx, nscoord dy, nscoord aHeight,
2439 nscolor aCursorColor)
2441 nsPoint pnts[4];
2442 nscoord ox = aHeight / 4;
2443 nscoord oy = ox;
2444 nscoord x0 = dx;
2445 nscoord y0 = dy + aHeight;
2446 pnts[0].x = x0 - ox;
2447 pnts[0].y = y0;
2448 pnts[1].x = x0;
2449 pnts[1].y = y0 - oy;
2450 pnts[2].x = x0 + ox;
2451 pnts[2].y = y0;
2452 pnts[3].x = x0 - ox;
2453 pnts[3].y = y0;
2455 // Draw little blue triangle
2456 aRenderingContext.SetColor(aCursorColor);
2457 aRenderingContext.FillPolygon(pnts, 4);
2460 #endif
2462 void
2463 nsTextFrame::PaintTextDecorations(nsIRenderingContext& aRenderingContext,
2464 nsStyleContext* aStyleContext,
2465 nsPresContext* aPresContext,
2466 nsTextPaintStyle& aTextStyle,
2467 nscoord aX, nscoord aY, nscoord aWidth,
2468 PRBool aRightToLeftText,
2469 PRUnichar *aText, /*=nsnull*/
2470 SelectionDetails *aDetails,/*= nsnull*/
2471 PRUint32 aIndex, /*= 0*/
2472 PRUint32 aLength, /*= 0*/
2473 const nscoord* aSpacing /* = nsnull*/ )
2476 // Quirks mode text decoration are rendered by children; see bug 1777
2477 // In non-quirks mode, nsHTMLContainer::Paint and nsBlockFrame::Paint
2478 // does the painting of text decorations.
2479 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
2480 nscolor overColor, underColor, strikeColor;
2482 PRBool useOverride = PR_FALSE;
2483 nscolor overrideColor;
2485 PRUint8 decorations = NS_STYLE_TEXT_DECORATION_NONE;
2486 // A mask of all possible decorations.
2487 PRUint8 decorMask = NS_STYLE_TEXT_DECORATION_UNDERLINE |
2488 NS_STYLE_TEXT_DECORATION_OVERLINE |
2489 NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
2490 nsStyleContext* context = aStyleContext;
2491 PRBool hasDecorations = context->HasTextDecorations();
2493 while (hasDecorations) {
2494 const nsStyleTextReset* styleText = context->GetStyleTextReset();
2495 if (!useOverride &&
2496 (NS_STYLE_TEXT_DECORATION_OVERRIDE_ALL &
2497 styleText->mTextDecoration)) {
2498 // This handles the <a href="blah.html"><font color="green">La
2499 // la la</font></a> case. The link underline should be green.
2500 useOverride = PR_TRUE;
2501 overrideColor = context->GetStyleColor()->mColor;
2504 PRUint8 useDecorations = decorMask & styleText->mTextDecoration;
2505 if (useDecorations) {// a decoration defined here
2506 nscolor color = context->GetStyleColor()->mColor;
2508 if (NS_STYLE_TEXT_DECORATION_UNDERLINE & useDecorations) {
2509 underColor = useOverride ? overrideColor : color;
2510 decorMask &= ~NS_STYLE_TEXT_DECORATION_UNDERLINE;
2511 decorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE;
2513 if (NS_STYLE_TEXT_DECORATION_OVERLINE & useDecorations) {
2514 overColor = useOverride ? overrideColor : color;
2515 decorMask &= ~NS_STYLE_TEXT_DECORATION_OVERLINE;
2516 decorations |= NS_STYLE_TEXT_DECORATION_OVERLINE;
2518 if (NS_STYLE_TEXT_DECORATION_LINE_THROUGH & useDecorations) {
2519 strikeColor = useOverride ? overrideColor : color;
2520 decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
2521 decorations |= NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
2524 if (0 == decorMask)
2525 break;
2526 context = context->GetParent();
2527 if (!context)
2528 break;
2529 hasDecorations = context->HasTextDecorations();
2532 nscoord offset;
2533 nscoord size;
2534 nscoord baseline = mAscent;
2535 if (decorations & (NS_FONT_DECORATION_OVERLINE |
2536 NS_FONT_DECORATION_UNDERLINE)) {
2537 aTextStyle.mNormalFont->GetUnderline(offset, size);
2538 if (decorations & NS_FONT_DECORATION_OVERLINE) {
2539 aRenderingContext.SetColor(overColor);
2540 aRenderingContext.FillRect(aX, aY, aWidth, size);
2542 if (decorations & NS_FONT_DECORATION_UNDERLINE) {
2543 aRenderingContext.SetColor(underColor);
2544 aRenderingContext.FillRect(aX, aY + baseline - offset, aWidth, size);
2547 if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
2548 aTextStyle.mNormalFont->GetStrikeout(offset, size);
2549 aRenderingContext.SetColor(strikeColor);
2550 aRenderingContext.FillRect(aX, aY + baseline - offset, aWidth, size);
2554 if (aDetails){
2555 nsRect rect = GetRect();
2556 while(aDetails){
2557 const nscoord* sp= aSpacing;
2558 PRInt32 startOffset = 0;
2559 PRInt32 textWidth = 0;
2560 PRInt32 start = PR_MAX(0,(aDetails->mStart - (PRInt32)aIndex));
2561 PRInt32 end = PR_MIN((PRInt32)aLength,(aDetails->mEnd - (PRInt32)aIndex));
2562 PRInt32 i;
2563 if ((start < end) && ((aLength - start) > 0))
2565 //aDetails allready processed to have offsets from frame start not content offsets
2566 if (start < end){
2567 if (aLength == 1)
2568 textWidth = aWidth;
2569 else {
2570 if (aDetails->mStart > 0){
2571 if (sp)
2573 for (i = 0; i < start;i ++){
2574 startOffset += *sp ++;
2577 else
2578 aRenderingContext.GetWidth(aText, start, startOffset);
2580 if (sp){
2581 for (i = start; i < end;i ++){
2582 textWidth += *sp ++;
2585 else
2586 aRenderingContext.GetWidth(aText + start,
2587 PRUint32(end - start), textWidth);
2590 nscolor lineColor;
2591 float relativeSize;
2592 nscoord offset, size;
2593 nscoord baseline = mAscent;
2594 switch (aDetails->mType) {
2595 case nsISelectionController::SELECTION_NORMAL:
2596 break;
2597 case nsISelectionController::SELECTION_SPELLCHECK:
2598 aTextStyle.mNormalFont->GetUnderline(offset, size);
2599 aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
2600 aRenderingContext.SetColor(NS_RGB(255,0,0));
2602 * If the rendering context is drawing text from right to left,
2603 * reverse the coordinates of the underline to match.
2605 if (aRightToLeftText) {
2606 nscoord rightEdge = aX + aWidth;
2607 aRenderingContext.DrawLine(rightEdge - textWidth - startOffset,
2608 aY + baseline - offset,
2609 rightEdge - startOffset,
2610 aY + baseline - offset);
2612 else {
2613 aRenderingContext.DrawLine(aX + startOffset,
2614 aY + baseline - offset,
2615 aX + startOffset + textWidth,
2616 aY + baseline - offset);
2618 break;
2619 case nsISelectionController::SELECTION_IME_RAWINPUT:
2620 case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
2621 case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
2622 case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
2623 if (aTextStyle.GetIMEUnderline(aDetails->mType,
2624 &lineColor,
2625 &relativeSize)) {
2626 aTextStyle.mNormalFont->GetUnderline(offset, size);
2627 aRenderingContext.SetColor(lineColor);
2629 * If the rendering context is drawing text from right to left,
2630 * reverse the coordinates of the underline to match.
2632 nscoord leftEdge = aRightToLeftText ?
2633 aX + aWidth - startOffset - textWidth + size :
2634 aX + startOffset + size;
2635 aRenderingContext.FillRect(leftEdge,
2636 aY + baseline - offset,
2637 textWidth - 2 * size,
2638 (nscoord)(relativeSize * size));
2640 break;
2641 default:
2642 NS_ASSERTION(0,"what type of selection do i not know about?");
2643 break;
2647 aDetails = aDetails->mNext;
2654 nsresult
2655 nsTextFrame::GetContentAndOffsetsForSelection(nsPresContext *aPresContext, nsIContent **aContent, PRInt32 *aOffset, PRInt32 *aLength)
2657 if (!aContent || !aOffset || !aLength)
2658 return NS_ERROR_NULL_POINTER;
2659 //ARE WE GENERATED??
2660 *aContent = nsnull;
2661 *aOffset = mContentOffset;
2662 *aLength = mContentLength;
2663 nsIFrame *parent = GetParent();
2664 if (parent)
2666 if ((mState & NS_FRAME_GENERATED_CONTENT) != 0)//parent is generated so so are we.
2668 //we COULD check the previous sibling but I dont think that is reliable
2669 *aContent = parent->GetContent();
2670 if(!*aContent)
2671 return NS_ERROR_FAILURE;
2672 NS_ADDREF(*aContent);
2674 //ARE WE A BEFORE FRAME? if not then we assume we are an after frame. this may be bad later
2675 nsIFrame *grandParent = parent->GetParent();
2676 if (grandParent)
2678 nsIFrame *firstParent = grandParent->GetFirstChild(nsnull);
2679 if (firstParent)
2681 *aLength = 0;
2682 if (firstParent == parent) //then our parent is the first child of granddad. use BEFORE
2684 *aOffset = 0;
2686 else
2688 *aOffset = (*aContent)->GetChildCount();
2691 else
2692 return NS_OK;
2696 //END GENERATED BLOCK
2697 if (!*aContent)
2699 *aContent = mContent;
2700 NS_IF_ADDREF(*aContent);
2703 return NS_OK;
2706 //---------------------------------------------------------
2707 nsresult nsTextFrame::GetTextInfoForPainting(nsPresContext* aPresContext,
2708 nsIPresShell** aPresShell,
2709 nsISelectionController** aSelectionController,
2710 PRBool& aDisplayingSelection,
2711 PRBool& aIsPaginated,
2712 PRBool& aIsSelected,
2713 PRBool& aHideStandardSelection,
2714 PRInt16& aSelectionValue)
2716 NS_ENSURE_ARG_POINTER(aPresContext);
2717 NS_ENSURE_ARG_POINTER(aPresShell);
2718 NS_ENSURE_ARG_POINTER(aSelectionController);
2720 //get the presshell
2721 NS_IF_ADDREF(*aPresShell = aPresContext->GetPresShell());
2722 if (!*aPresShell)
2723 return NS_ERROR_FAILURE;
2725 //get the selection controller
2726 nsresult rv = GetSelectionController(aPresContext, aSelectionController);
2727 if (NS_FAILED(rv) || !(*aSelectionController))
2728 return NS_ERROR_FAILURE;
2730 (*aSelectionController)->GetDisplaySelection(&aSelectionValue);
2732 if (aPresContext->IsRenderingOnlySelection()) {
2733 aIsPaginated = PR_TRUE;
2734 aDisplayingSelection = PR_TRUE;
2735 } else {
2736 aIsPaginated = PR_FALSE;
2737 aDisplayingSelection =
2738 (aSelectionValue > nsISelectionController::SELECTION_HIDDEN);
2741 PRInt16 textSel=0;
2742 (*aSelectionController)->GetSelectionFlags(&textSel);
2743 if (!(textSel & nsISelectionDisplay::DISPLAY_TEXT))
2744 aDisplayingSelection = PR_FALSE;
2746 // the spellcheck selection should be visible all the time
2747 aHideStandardSelection = !aDisplayingSelection;
2748 if (!aDisplayingSelection){
2749 nsCOMPtr<nsISelection> spellcheckSelection;
2750 (*aSelectionController)->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
2751 getter_AddRefs(spellcheckSelection));
2752 if (spellcheckSelection){
2753 PRBool iscollapsed = PR_FALSE;
2754 spellcheckSelection->GetIsCollapsed(&iscollapsed);
2755 if (!iscollapsed)
2756 aDisplayingSelection = PR_TRUE;
2760 // Transform text from content into renderable form
2761 // XXX If the text fragment is already Unicode and the text wasn't
2762 // transformed when we formatted it, then there's no need to do all
2763 // this and we should just render the text fragment directly. See
2764 // PaintAsciiText()...
2765 nsIDocument *doc = (*aPresShell)->GetDocument();
2766 if (!doc)
2767 return NS_ERROR_FAILURE;
2769 aIsSelected = (GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
2771 return NS_OK;
2774 nsresult
2775 nsTextFrame::GetSelectionStatus(nsPresContext* aPresContext,
2776 PRInt16& aSelectionValue)
2778 NS_ENSURE_ARG_POINTER(aPresContext);
2780 // get the selection controller
2781 nsCOMPtr<nsISelectionController> selectionController;
2782 nsresult rv = GetSelectionController(aPresContext,
2783 getter_AddRefs(selectionController));
2784 if (NS_FAILED(rv) || !selectionController)
2785 return NS_ERROR_FAILURE;
2787 selectionController->GetDisplaySelection(&aSelectionValue);
2789 return NS_OK;
2792 PRBool
2793 nsTextFrame::IsTextInSelection()
2795 nsCOMPtr<nsISelectionController> selCon;
2796 nsCOMPtr<nsIPresShell> shell;
2797 PRBool displaySelection;
2798 PRBool isPaginated;
2799 PRBool isSelected;
2800 PRBool hideStandardSelection;
2801 PRInt16 selectionValue;
2802 nsPresContext* presContext = PresContext();
2803 if (NS_FAILED(GetTextInfoForPainting(presContext,
2804 getter_AddRefs(shell),
2805 getter_AddRefs(selCon),
2806 displaySelection,
2807 isPaginated,
2808 isSelected,
2809 hideStandardSelection,
2810 selectionValue))) {
2811 return PR_FALSE;
2814 // Make enough space to transform
2815 nsAutoTextBuffer paintBuffer;
2816 nsAutoIndexBuffer indexBuffer;
2817 if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) {
2818 return PR_FALSE;
2821 // Transform text from content into renderable form
2822 // XXX If the text fragment is already Unicode and the text wasn't
2823 // transformed when we formatted it, then there's no need to do all
2824 // this and we should just render the text fragment directly. See
2825 // PaintAsciiText()...
2827 nsTextTransformer tx(presContext);
2828 PRInt32 textLength;
2829 // no need to worry about justification, that's always on the slow path
2830 PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
2832 PRInt32* ip = indexBuffer.mBuffer;
2833 PRUnichar* text = paintBuffer.mBuffer;
2835 isSelected = PR_FALSE;
2836 if (0 != textLength) {
2838 SelectionDetails *details = nsnull;
2840 nsCOMPtr<nsIContent> content;
2841 PRInt32 offset;
2842 PRInt32 length;
2844 nsresult rv = GetContentAndOffsetsForSelection(presContext,
2845 getter_AddRefs(content),
2846 &offset, &length);
2847 if (NS_SUCCEEDED(rv) && content) {
2848 details = GetFrameSelection()->LookUpSelection(content, mContentOffset,
2849 mContentLength, PR_FALSE);
2852 //where are the selection points "really"
2853 SelectionDetails *sdptr = details;
2854 while (sdptr){
2855 sdptr->mStart = ip[sdptr->mStart] - mContentOffset;
2856 sdptr->mEnd = ip[sdptr->mEnd] - mContentOffset;
2857 sdptr = sdptr->mNext;
2859 //while we have substrings...
2860 //PRBool drawn = PR_FALSE;
2861 DrawSelectionIterator iter(details, text, (PRUint32)textLength, nsnull,
2862 nsTextPaintStyle::eNormalSelection);
2863 if (!iter.IsDone() && iter.First()) {
2864 isSelected = PR_TRUE;
2867 sdptr = details;
2868 if (details) {
2869 while ((sdptr = details->mNext) != nsnull) {
2870 delete details;
2871 details = sdptr;
2873 delete details;
2876 return isSelected;
2879 PRBool
2880 nsTextFrame::IsVisibleInSelection(nsISelection* aSelection)
2882 // Check the quick way first
2883 PRBool isSelected = (mState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
2884 if (!isSelected)
2885 return PR_FALSE;
2887 return IsTextInSelection();
2890 void
2891 nsTextFrame::PaintUnicodeText(nsPresContext* aPresContext,
2892 nsIRenderingContext& aRenderingContext,
2893 nsStyleContext* aStyleContext,
2894 nsTextPaintStyle& aTextStyle,
2895 nscoord dx, nscoord dy)
2897 nsCOMPtr<nsISelectionController> selCon;
2898 nsCOMPtr<nsIPresShell> shell;
2899 PRBool displaySelection,canDarkenColor=PR_FALSE;
2900 PRBool isPaginated;
2901 PRBool isSelected;
2902 PRBool hideStandardSelection;
2903 PRInt16 selectionValue;
2904 #ifdef IBMBIDI
2905 PRBool isOddLevel = PR_FALSE;
2906 #endif
2908 if (NS_FAILED(GetTextInfoForPainting(aPresContext,
2909 getter_AddRefs(shell),
2910 getter_AddRefs(selCon),
2911 displaySelection,
2912 isPaginated,
2913 isSelected,
2914 hideStandardSelection,
2915 selectionValue))) {
2916 return;
2919 if(isPaginated){
2920 canDarkenColor = CanDarken(aPresContext);
2923 // Make enough space to transform
2924 nsAutoTextBuffer paintBuffer;
2925 nsAutoIndexBuffer indexBuffer;
2926 if (displaySelection) {
2927 if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) {
2928 return;
2931 nscoord width = mRect.width;
2933 // Transform text from content into renderable form
2934 // XXX If the text fragment is already Unicode and the text wasn't
2935 // transformed when we formatted it, then there's no need to do all
2936 // this and we should just render the text fragment directly. See
2937 // PaintAsciiText()...
2939 nsTextTransformer tx(aPresContext);
2940 PRInt32 textLength;
2942 // In whitespace:-moz-pre-wrap it's possible that we trimmed multiple
2943 // spaces from the end of line because they did not fit in the line.
2944 // In that case, all those spaces need to be removed before painting.
2945 PRBool removeMultipleTrimmedWS = NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == GetStyleText()->mWhiteSpace;
2947 // no need to worry about justification, that's always on the slow path
2948 PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull),
2949 &paintBuffer, &textLength, PR_FALSE, nsnull, removeMultipleTrimmedWS);
2951 PRInt32* ip = indexBuffer.mBuffer;
2952 PRUnichar* text = paintBuffer.mBuffer;
2954 if (0 != textLength)
2956 #ifdef IBMBIDI
2957 PRBool isRightToLeftOnBidiPlatform = PR_FALSE;
2958 PRBool isBidiSystem = PR_FALSE;
2959 nsCharType charType = eCharType_LeftToRight;
2960 if (aPresContext->BidiEnabled()) {
2961 isBidiSystem = aPresContext->IsBidiSystem();
2962 isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
2963 charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsGkAtoms::charType));
2965 isRightToLeftOnBidiPlatform = (isBidiSystem &&
2966 (eCharType_RightToLeft == charType ||
2967 eCharType_RightToLeftArabic == charType));
2968 if (isRightToLeftOnBidiPlatform) {
2969 // indicate that the platform should use its native
2970 // capabilities to reorder the text with right-to-left
2971 // base direction
2972 aRenderingContext.SetRightToLeftText(PR_TRUE);
2974 nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils();
2975 if (bidiUtils) {
2976 #ifdef DEBUG
2977 PRInt32 rememberTextLength = textLength;
2978 #endif
2979 PRUint32 hints = 0;
2980 aRenderingContext.GetHints(hints);
2981 bidiUtils->ReorderUnicodeText(text, textLength,
2982 charType, isOddLevel, isBidiSystem,
2983 (hints & NS_RENDERING_HINT_NEW_TEXT_RUNS) != 0);
2984 NS_ASSERTION(rememberTextLength == textLength, "Bidi formatting changed text length");
2987 #endif // IBMBIDI
2988 if (!displaySelection || !isSelected ) //draw text normally
2990 // When there is no selection showing, use the fastest and
2991 // simplest rendering approach
2993 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
2994 aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
2995 PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
2996 aTextStyle, dx, dy, width, PR_FALSE);
2998 else
2999 { //we draw according to selection rules
3000 SelectionDetails *details = nsnull;
3001 nsCOMPtr<nsIContent> content;
3002 PRInt32 offset;
3003 PRInt32 length;
3004 nsresult rv = GetContentAndOffsetsForSelection(aPresContext,
3005 getter_AddRefs(content),
3006 &offset, &length);
3007 if (NS_SUCCEEDED(rv) && content) {
3008 details = GetFrameSelection()->LookUpSelection(content, mContentOffset,
3009 mContentLength, PR_FALSE);
3012 //where are the selection points "really"
3013 SelectionDetails *sdptr = details;
3014 while (sdptr){
3015 sdptr->mStart = ip[sdptr->mStart] - mContentOffset;
3016 sdptr->mEnd = ip[sdptr->mEnd] - mContentOffset;
3017 #ifdef SUNCTL
3018 nsCOMPtr<nsILE> ctlObj;
3019 ctlObj = do_CreateInstance(kLECID, &rv);
3020 if (NS_FAILED(rv)) {
3021 NS_WARNING("Cell based cursor movement will not be supported\n");
3022 ctlObj = nsnull;
3024 else {
3025 PRInt32 start, end;
3026 PRBool needsCTL = PR_FALSE;
3028 ctlObj->NeedsCTLFix(text, sdptr->mStart, sdptr->mEnd, &needsCTL);
3030 if (needsCTL && (sdptr->mEnd < textLength)) {
3031 ctlObj->GetRangeOfCluster(text, PRInt32(textLength), sdptr->mEnd,
3032 &start, &end);
3033 if (sdptr->mStart > sdptr->mEnd) /* Left Edge */
3034 sdptr->mEnd = start;
3035 else
3036 sdptr->mEnd = end;
3039 /* Always start selection from a Right Edge */
3040 if (needsCTL && (sdptr->mStart > 0)) {
3041 ctlObj->GetRangeOfCluster(text, PRInt32(textLength),
3042 sdptr->mStart, &start, &end);
3043 sdptr->mStart = end;
3046 #endif /* SUNCTL */
3047 #ifdef IBMBIDI
3048 AdjustSelectionPointsForBidi(sdptr, textLength, CHARTYPE_IS_RTL(charType), isOddLevel, isBidiSystem);
3049 #endif
3050 sdptr = sdptr->mNext;
3052 if (!hideStandardSelection || displaySelection) {
3054 * Text is drawn by drawing the entire string every time, but
3055 * using clip regions to control which part of the text is shown
3056 * (selected or unselected.) We do this because you can't
3057 * assume that the layout of a part of text will be the same
3058 * when it's drawn apart from the entire string. This is true
3059 * in languages like arabic, where shaping affects entire words.
3060 * Simply put: length("abcd") != length("ab") + length("cd") in
3061 * some languages.
3064 // See if this rendering backend supports getting cluster
3065 // information.
3066 PRUint32 clusterHint = 0;
3067 aRenderingContext.GetHints(clusterHint);
3068 clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
3070 //while we have substrings...
3071 //PRBool drawn = PR_FALSE;
3072 DrawSelectionIterator iter(details, text, (PRUint32)textLength, &aTextStyle,
3073 nsTextPaintStyle::eAllSelections);
3074 if (!iter.IsDone() && iter.First())
3076 nscoord currentX = dx;
3077 nscoord newWidth;//temp
3078 #ifdef IBMBIDI // Simon - display substrings RTL in RTL frame
3079 nscoord FrameWidth = 0;
3080 if (isRightToLeftOnBidiPlatform)
3081 if (NS_SUCCEEDED(aRenderingContext.GetWidth(text, textLength, FrameWidth)))
3082 currentX = dx + FrameWidth;
3083 #endif
3084 while (!iter.IsDone())
3086 PRUnichar *currenttext = iter.CurrentTextUnicharPtr();
3087 PRUint32 currentlength= iter.CurrentLength();
3088 nscolor currentFGColor, currentBKColor;
3089 PRBool isCurrentBKColorTransparent;
3091 PRBool isSelection = iter.GetSelectionColors(&currentFGColor,
3092 &currentBKColor,
3093 &isCurrentBKColorTransparent);
3095 if (currentlength > 0)
3097 if (clusterHint) {
3098 PRUint32 tmpWidth;
3099 rv = aRenderingContext.GetRangeWidth(text, textLength, currenttext - text,
3100 (currenttext - text) + currentlength,
3101 tmpWidth);
3102 newWidth = nscoord(tmpWidth);
3104 else {
3105 rv = aRenderingContext.GetWidth(currenttext, currentlength,newWidth); //ADJUST FOR CHAR SPACING
3107 if (NS_SUCCEEDED(rv)) {
3108 if (isRightToLeftOnBidiPlatform)
3109 currentX -= newWidth;
3110 if (isSelection && !isPaginated)
3111 {//DRAW RECT HERE!!!
3112 if (!isCurrentBKColorTransparent) {
3113 aRenderingContext.SetColor(currentBKColor);
3114 aRenderingContext.FillRect(currentX, dy, newWidth, mRect.height);
3118 else {
3119 newWidth = 0;
3122 else {
3123 newWidth = 0;
3126 aRenderingContext.PushState();
3128 nsRect rect(currentX, dy, newWidth, mRect.height);
3129 aRenderingContext.SetClipRect(rect, nsClipCombine_kIntersect);
3131 if (isPaginated && !iter.IsBeforeOrAfter()) {
3132 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
3133 aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
3134 } else if (!isPaginated) {
3135 aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor,canDarkenColor));
3136 aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
3139 aRenderingContext.PopState();
3141 #ifdef IBMBIDI
3142 if (!isRightToLeftOnBidiPlatform)
3143 #endif
3144 currentX += newWidth; // increment twips X start
3146 iter.Next();
3149 else if (!isPaginated)
3151 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
3152 aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
3155 PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
3156 aTextStyle, dx, dy, width,
3157 isRightToLeftOnBidiPlatform, text, details, 0,
3158 (PRUint32)textLength, nsnull);
3159 sdptr = details;
3160 if (details){
3161 while ((sdptr = details->mNext) != nsnull) {
3162 delete details;
3163 details = sdptr;
3165 delete details;
3168 #ifdef IBMBIDI
3169 if (isRightToLeftOnBidiPlatform) {
3170 // indicate that future text should not be reordered with
3171 // right-to-left base direction
3172 aRenderingContext.SetRightToLeftText(PR_FALSE);
3174 #endif // IBMBIDI
3178 //measure Spaced Textvoid
3179 nsresult
3180 nsTextFrame::GetPositionSlowly(nsIRenderingContext* aRendContext,
3181 const nsPoint& aPoint,
3182 nsIContent** aNewContent,
3183 PRInt32& aOffset)
3186 // pre-condition tests
3187 NS_PRECONDITION(aRendContext && aNewContent, "null arg");
3188 if (!aRendContext || !aNewContent) {
3189 return NS_ERROR_NULL_POINTER;
3191 // initialize out param
3192 *aNewContent = nsnull;
3194 nsPresContext* presContext = PresContext();
3195 nsTextStyle ts(presContext, *aRendContext, mStyleContext);
3196 SetupTextRunDirection(presContext, aRendContext);
3197 if (!ts.mSmallCaps && !ts.mWordSpacing && !ts.mLetterSpacing && !ts.mJustifying) {
3198 return NS_ERROR_INVALID_ARG;
3201 /* This if clause is the cause of much pain. If aNewContent is set, then any
3202 * code path that returns an error must set aNewContent to null before returning,
3203 * or risk the caller unknowingly decrementing aNewContent inappropriately.
3204 * Here's what Robert O'Callahan has to say on the matter:
3205 If I'm not mistaken, in GetPositionSlowly, the values of aNewContent and
3206 aOffset set in the conditional "if (aPoint.x - origin.x < 0)" are
3207 overwritten on all successful return paths. Since they should never be
3208 used by the caller if the function fails, that entire "if" statement is
3209 --- or should be --- a no-op. Come to think of it, it doesn't make sense
3210 either; setting aOffset to zero is nonsense.
3212 I recommend you just delete that "if" statement.
3214 * If this clause is removed, then some of the bullet-proofing code
3215 * prefaced with "bug 56704" comments can be removed as well.
3217 if (aPoint.x < 0)
3219 *aNewContent = mContent;
3220 aOffset =0;
3223 // Make enough space to transform
3224 nsAutoTextBuffer paintBuffer;
3225 nsAutoIndexBuffer indexBuffer;
3226 nsresult rv = indexBuffer.GrowTo(mContentLength + 1);
3227 if (NS_FAILED(rv)) {
3228 // If we've already assigned aNewContent, make sure to 0 it out here.
3229 // See bug 56704.
3230 *aNewContent = nsnull;
3231 return rv;
3234 // Transform text from content into renderable form
3235 nsTextTransformer tx(PresContext());
3236 PRInt32 textLength;
3237 PRIntn numJustifiableCharacter;
3239 PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength, PR_TRUE, &numJustifiableCharacter);
3240 if (textLength <= 0) {
3241 // If we've already assigned aNewContent, make sure to 0 it out here.
3242 // aNewContent is undefined in the case that we return a failure,
3243 // If we were to return a valid pointer, we risk decrementing that node's
3244 // ref count an extra time by the caller.
3245 // See bug 56704 for more details.
3246 *aNewContent = nsnull;
3247 return NS_ERROR_FAILURE;
3250 #ifdef IBMBIDI // Simon -- reverse RTL text here
3251 PRBool isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
3252 if (isOddLevel) {
3253 PRUnichar *tStart, *tEnd;
3254 PRUnichar tSwap;
3255 for (tStart = paintBuffer.mBuffer, tEnd = tStart + textLength - 1; tEnd > tStart; tStart++, tEnd--) {
3256 tSwap = *tStart;
3257 *tStart = *tEnd;
3258 *tEnd = tSwap;
3261 #endif // IBMBIDI
3263 ComputeExtraJustificationSpacing(*aRendContext, ts, paintBuffer.mBuffer, textLength, numJustifiableCharacter);
3265 //the following will first get the index into the PAINTBUFFER then the actual content
3266 nscoord adjustedX = PR_MAX(0,aPoint.x);
3268 #ifdef IBMBIDI
3269 if (isOddLevel)
3270 aOffset = mContentOffset + textLength -
3271 GetLengthSlowly(*aRendContext, ts, paintBuffer.mBuffer,
3272 textLength, PR_TRUE, adjustedX);
3273 else
3274 #endif
3275 aOffset = mContentOffset +
3276 GetLengthSlowly(*aRendContext, ts,paintBuffer.mBuffer,
3277 textLength, PR_TRUE, adjustedX);
3278 PRInt32 i;
3279 PRInt32* ip = indexBuffer.mBuffer;
3280 for (i = 0;i <= mContentLength; i ++){
3281 if ((ip[i] >= aOffset) && //reverse mapping
3282 (! NS_IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) {
3283 aOffset = i + mContentOffset;
3284 break;
3289 *aNewContent = mContent;
3290 if (*aNewContent)
3291 (*aNewContent)->AddRef();
3292 return NS_OK;
3295 void
3296 nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext,
3297 nsStyleContext* aStyleContext,
3298 nsPresContext* aPresContext,
3299 nsTextPaintStyle& aTextStyle,
3300 PRBool aRightToLeftText,
3301 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
3302 nscoord aX, nscoord aY,
3303 nscoord aWidth,
3304 SelectionDetails *aDetails /*=nsnull*/)
3306 PRUnichar buf[TEXT_BUF_SIZE];
3307 PRUnichar* bp0 = buf;
3309 nscoord spacingMem[TEXT_BUF_SIZE];
3310 nscoord* sp0 = spacingMem;
3312 PRBool spacing = (0 != aTextStyle.mLetterSpacing) ||
3313 (0 != aTextStyle.mWordSpacing) || aTextStyle.mJustifying;
3315 PRBool justifying = aTextStyle.mJustifying &&
3316 (aTextStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aTextStyle.mExtraSpacePerJustifiableCharacter != 0);
3318 PRBool isCJ = IsChineseJapaneseLangGroup();
3319 PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState);
3321 //German 0x00df might expand to "SS", but no need to count it for speed reason
3322 if (aTextStyle.mSmallCaps) {
3323 if (aLength*2 > TEXT_BUF_SIZE) {
3324 bp0 = new PRUnichar[aLength*2];
3325 if (spacing)
3326 sp0 = new nscoord[aLength*2];
3329 else if (aLength > TEXT_BUF_SIZE) {
3330 bp0 = new PRUnichar[aLength];
3331 if (spacing)
3332 sp0 = new nscoord[aLength];
3335 PRUnichar* bp = bp0;
3336 nscoord* sp = sp0;
3338 nsIFontMetrics* lastFont = aTextStyle.mLastFont;
3339 PRInt32 pendingCount;
3340 PRUnichar* runStart = bp;
3341 nscoord charWidth, width = 0;
3342 PRInt32 countSoFar = 0;
3343 // Save the color we want to use for the text, since calls to
3344 // PaintTextDecorations in this method will call SetColor() on the rendering
3345 // context.
3346 nscolor textColor;
3347 aRenderingContext.GetColor(textColor);
3348 for (; --aLength >= 0; aBuffer++) {
3349 nsIFontMetrics* nextFont;
3350 nscoord glyphWidth = 0;
3351 PRUnichar ch = *aBuffer;
3352 if (aTextStyle.mSmallCaps &&
3353 (IsLowerCase(ch) || (ch == kSZLIG))) {
3354 nextFont = aTextStyle.mSmallFont;
3356 else {
3357 nextFont = aTextStyle.mNormalFont;
3359 if (nextFont != lastFont) {
3360 pendingCount = bp - runStart;
3361 if (0 != pendingCount) {
3362 // Render the text with the color specified first.
3363 aRenderingContext.SetColor(textColor);
3364 // Measure previous run of characters using the previous font
3365 aRenderingContext.DrawString(runStart, pendingCount,
3366 aX, aY + mAscent, -1,
3367 spacing ? sp0 : nsnull);
3369 // Note: use aY not small-y so that decorations are drawn with
3370 // respect to the normal-font not the current font.
3371 PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
3372 aTextStyle, aX, aY, width,
3373 aRightToLeftText, runStart, aDetails,
3374 countSoFar, pendingCount, spacing ? sp0 : nsnull);
3375 countSoFar += pendingCount;
3376 aWidth -= width;
3377 aX += width;
3378 runStart = bp = bp0;
3379 sp = sp0;
3380 width = 0;
3382 aRenderingContext.SetFont(nextFont);
3383 lastFont = nextFont;
3385 if (nextFont == aTextStyle.mSmallFont) {
3386 PRUnichar upper_ch;
3387 // German szlig should be expanded to "SS".
3388 if (ch == kSZLIG)
3389 upper_ch = (PRUnichar)'S';
3390 else
3391 upper_ch = ToUpperCase(ch);
3392 aRenderingContext.GetWidth(upper_ch, charWidth);
3393 glyphWidth += charWidth + aTextStyle.mLetterSpacing;
3394 if (ch == kSZLIG) //add an additional 'S' here.
3396 *bp++ = upper_ch;
3397 if (spacing)
3398 *sp++ = glyphWidth;
3399 width += glyphWidth;
3401 ch = upper_ch;
3403 else if (ch == ' ') {
3404 glyphWidth += aTextStyle.mSpaceWidth + aTextStyle.mWordSpacing + aTextStyle.mLetterSpacing;
3406 else if (NS_IS_HIGH_SURROGATE(ch) && aLength > 0 &&
3407 NS_IS_LOW_SURROGATE(*(aBuffer+1))) {
3409 // special handling for surrogate pair
3410 aRenderingContext.GetWidth(aBuffer, 2, charWidth);
3411 glyphWidth += charWidth + aTextStyle.mLetterSpacing;
3412 // copy the surrogate low
3413 *bp++ = ch;
3414 --aLength;
3415 aBuffer++;
3416 ch = *aBuffer;
3417 // put the width into the space buffer
3418 width += glyphWidth;
3419 if (spacing)
3420 *sp++ = glyphWidth;
3421 // set the glyphWidth to 0 so the code later will
3422 // set a 0 for one element in space array for surrogate low to 0
3423 glyphWidth = 0;
3425 else {
3426 aRenderingContext.GetWidth(ch, charWidth);
3427 glyphWidth += charWidth + aTextStyle.mLetterSpacing;
3429 if (justifying && (!isEndOfLine || aLength > 0)
3430 && IsJustifiableCharacter(ch, isCJ)) {
3431 glyphWidth += aTextStyle.mExtraSpacePerJustifiableCharacter;
3432 if ((PRUint32)--aTextStyle.mNumJustifiableCharacterToRender
3433 < (PRUint32)aTextStyle.mNumJustifiableCharacterReceivingExtraJot) {
3434 glyphWidth++;
3437 *bp++ = ch;
3438 if (spacing)
3439 *sp++ = glyphWidth;
3440 width += glyphWidth;
3442 pendingCount = bp - runStart;
3443 if (0 != pendingCount) {
3444 // Render the text with the color specified first.
3445 aRenderingContext.SetColor(textColor);
3446 // Measure previous run of characters using the previous font
3447 aRenderingContext.DrawString(runStart, pendingCount, aX, aY + mAscent, -1,
3448 spacing ? sp0 : nsnull);
3450 // Note: use aY not small-y so that decorations are drawn with
3451 // respect to the normal-font not the current font.
3452 PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
3453 aTextStyle, aX, aY, aWidth,
3454 aRightToLeftText, runStart, aDetails,
3455 countSoFar, pendingCount, spacing ? sp0 : nsnull);
3457 aTextStyle.mLastFont = lastFont;
3459 if (bp0 != buf) {
3460 delete [] bp0;
3462 if (sp0 != spacingMem) {
3463 delete [] sp0;
3467 inline void
3468 nsTextFrame::MeasureSmallCapsText(nsIRenderingContext* aRenderingContext,
3469 nsTextStyle& aTextStyle,
3470 PRUnichar* aWord,
3471 PRInt32 aWordLength,
3472 PRBool aIsEndOfFrame,
3473 nsTextDimensions* aDimensionsResult)
3475 aDimensionsResult->Clear();
3476 GetTextDimensions(*aRenderingContext, aTextStyle, aWord, aWordLength,
3477 aIsEndOfFrame, aDimensionsResult);
3478 if (aTextStyle.mLastFont != aTextStyle.mNormalFont) {
3479 aRenderingContext->SetFont(aTextStyle.mNormalFont);
3480 aTextStyle.mLastFont = aTextStyle.mNormalFont;
3485 PRInt32
3486 nsTextFrame::GetTextDimensionsOrLength(nsIRenderingContext& aRenderingContext,
3487 nsTextStyle& aStyle,
3488 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
3489 nsTextDimensions* aDimensionsResult,
3490 PRBool aGetTextDimensions/* true=get dimensions false = return length up to aDimensionsResult.width size*/)
3492 PRUnichar *inBuffer = aBuffer;
3493 PRInt32 length = aLength;
3494 nsAutoTextBuffer dimensionsBuffer;
3495 if (NS_FAILED(dimensionsBuffer.GrowTo(length))) {
3496 aDimensionsResult->Clear();
3497 return 0;
3499 PRUnichar* bp = dimensionsBuffer.mBuffer;
3501 nsIFontMetrics* lastFont = aStyle.mLastFont;
3502 nsTextDimensions sum, glyphDimensions;
3503 PRBool justifying = aStyle.mJustifying &&
3504 (aStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aStyle.mExtraSpacePerJustifiableCharacter != 0);
3505 PRBool isCJ = IsChineseJapaneseLangGroup();
3506 PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState);
3508 for (PRInt32 prevLength = length; --length >= 0; prevLength = length) {
3509 PRUnichar ch = *inBuffer++;
3510 if (aStyle.mSmallCaps &&
3511 (IsLowerCase(ch) || (ch == kSZLIG))) {
3512 PRUnichar upper_ch;
3513 // German szlig should be expanded to "SS".
3514 if (ch == kSZLIG)
3515 upper_ch = (PRUnichar)'S';
3516 else
3517 upper_ch = ToUpperCase(ch);
3518 if (lastFont != aStyle.mSmallFont) {
3519 lastFont = aStyle.mSmallFont;
3520 aRenderingContext.SetFont(lastFont);
3522 aRenderingContext.GetTextDimensions(&upper_ch, (PRUint32)1, glyphDimensions);
3523 glyphDimensions.width += aStyle.mLetterSpacing;
3524 if (ch == kSZLIG)
3525 glyphDimensions.width += glyphDimensions.width;
3527 else if (ch == ' ' || ch == CH_NBSP) {
3528 glyphDimensions.width = aStyle.mSpaceWidth + aStyle.mLetterSpacing
3529 + aStyle.mWordSpacing;
3531 else {
3532 if (lastFont != aStyle.mNormalFont) {
3533 lastFont = aStyle.mNormalFont;
3534 aRenderingContext.SetFont(lastFont);
3536 if (NS_IS_HIGH_SURROGATE(ch) && length > 0 &&
3537 NS_IS_LOW_SURROGATE(*inBuffer)) {
3538 aRenderingContext.GetTextDimensions(inBuffer-1, (PRUint32)2, glyphDimensions);
3539 length--;
3540 inBuffer++;
3541 } else {
3542 aRenderingContext.GetTextDimensions(&ch, (PRUint32)1, glyphDimensions);
3544 glyphDimensions.width += aStyle.mLetterSpacing;
3546 if (justifying && (!isEndOfLine || length > 0)
3547 && IsJustifiableCharacter(ch, isCJ)) {
3548 glyphDimensions.width += aStyle.mExtraSpacePerJustifiableCharacter;
3549 if ((PRUint32)--aStyle.mNumJustifiableCharacterToMeasure
3550 < (PRUint32)aStyle.mNumJustifiableCharacterReceivingExtraJot) {
3551 ++glyphDimensions.width;
3554 sum.Combine(glyphDimensions);
3555 *bp++ = ch;
3556 if (!aGetTextDimensions && sum.width >= aDimensionsResult->width) {
3557 PRInt32 result = aLength - length;
3558 if (2*(sum.width - aDimensionsResult->width) > glyphDimensions.width) //then we have gone too far, back up 1
3559 result = aLength - prevLength;
3560 aStyle.mLastFont = lastFont;
3561 return result;
3564 aStyle.mLastFont = lastFont;
3565 *aDimensionsResult = sum;
3566 return aLength;
3570 // XXX factor in logic from RenderString into here; gaps, justification, etc.
3571 void
3572 nsTextFrame::GetTextDimensions(nsIRenderingContext& aRenderingContext,
3573 nsTextStyle& aTextStyle,
3574 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
3575 nsTextDimensions* aDimensionsResult)
3577 GetTextDimensionsOrLength(aRenderingContext,aTextStyle,
3578 aBuffer,aLength,aIsEndOfFrame,aDimensionsResult,PR_TRUE);
3581 PRInt32
3582 nsTextFrame::GetLengthSlowly(nsIRenderingContext& aRenderingContext,
3583 nsTextStyle& aStyle,
3584 PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame,
3585 nscoord aWidth)
3587 nsTextDimensions dimensions;
3588 dimensions.width = aWidth;
3589 return GetTextDimensionsOrLength(aRenderingContext,aStyle,
3590 aBuffer,aLength,aIsEndOfFrame,&dimensions,PR_FALSE);
3593 void
3594 nsTextFrame::ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext,
3595 nsTextStyle& aTextStyle,
3596 PRUnichar* aBuffer, PRInt32 aLength,
3597 PRInt32 aNumJustifiableCharacter)
3599 if (aTextStyle.mJustifying) {
3600 nsTextDimensions trueDimensions;
3602 // OK, so this is a bit ugly. The problem is that to get the right margin
3603 // nice and clean, we have to apply a little extra space to *some* of the
3604 // justifiable characters. It has to be the same ones every time or things will go haywire.
3605 // This implies that the GetTextDimensionsOrLength and RenderString functions depend
3606 // on a little bit of secret state: which part of the prepared text they are
3607 // looking at. It turns out that they get called in a regular way: they look
3608 // at the text from the beginning to the end. So we just count which justifiable character
3609 // we're up to, for each context.
3610 // This is not a great solution, but a perfect solution requires much more
3611 // widespread changes, to explicitly annotate all the transformed text fragments
3612 // that are passed around with their position in the transformed text
3613 // for the entire frame.
3614 aTextStyle.mNumJustifiableCharacterToMeasure = 0;
3615 aTextStyle.mExtraSpacePerJustifiableCharacter = 0;
3616 aTextStyle.mNumJustifiableCharacterReceivingExtraJot = 0;
3618 GetTextDimensions(aRenderingContext, aTextStyle, aBuffer, aLength, PR_TRUE, &trueDimensions);
3620 aTextStyle.mNumJustifiableCharacterToMeasure = aNumJustifiableCharacter;
3621 aTextStyle.mNumJustifiableCharacterToRender = aNumJustifiableCharacter;
3623 nscoord extraSpace = mRect.width - trueDimensions.width;
3625 if (extraSpace > 0 && aNumJustifiableCharacter > 0) {
3626 aTextStyle.mExtraSpacePerJustifiableCharacter = extraSpace/aNumJustifiableCharacter;
3627 aTextStyle.mNumJustifiableCharacterReceivingExtraJot =
3628 extraSpace - aTextStyle.mExtraSpacePerJustifiableCharacter*aNumJustifiableCharacter;
3633 void
3634 nsTextFrame::PaintTextSlowly(nsPresContext* aPresContext,
3635 nsIRenderingContext& aRenderingContext,
3636 nsStyleContext* aStyleContext,
3637 nsTextPaintStyle& aTextStyle,
3638 nscoord dx, nscoord dy)
3640 nsCOMPtr<nsISelectionController> selCon;
3641 nsCOMPtr<nsIPresShell> shell;
3642 PRBool displaySelection;
3643 PRBool isPaginated,canDarkenColor=PR_FALSE;
3644 PRBool isSelected;
3645 PRBool hideStandardSelection;
3646 PRInt16 selectionValue;
3647 if (NS_FAILED(GetTextInfoForPainting(aPresContext,
3648 getter_AddRefs(shell),
3649 getter_AddRefs(selCon),
3650 displaySelection,
3651 isPaginated,
3652 isSelected,
3653 hideStandardSelection,
3654 selectionValue))) {
3655 return;
3659 if(isPaginated){
3660 canDarkenColor = CanDarken(aPresContext);
3663 // Make enough space to transform
3664 nsAutoTextBuffer paintBuffer;
3665 nsAutoIndexBuffer indexBuffer;
3666 if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) {
3667 return;
3669 nscoord width = mRect.width;
3670 PRInt32 textLength;
3672 nsTextTransformer tx(aPresContext);
3673 PRIntn numJustifiableCharacter;
3675 PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull),
3676 &paintBuffer, &textLength, PR_TRUE, &numJustifiableCharacter);
3678 PRInt32* ip = indexBuffer.mBuffer;
3679 PRUnichar* text = paintBuffer.mBuffer;
3681 if (0 != textLength) {
3682 #ifdef IBMBIDI
3683 PRBool isRightToLeftOnBidiPlatform = PR_FALSE;
3684 PRBool isBidiSystem = PR_FALSE;
3685 PRBool isOddLevel = PR_FALSE;
3686 PRUint32 hints = 0;
3687 aRenderingContext.GetHints(hints);
3688 PRBool paintCharByChar = (0 == (hints & NS_RENDERING_HINT_REORDER_SPACED_TEXT)) &&
3689 ((0 != aTextStyle.mLetterSpacing) ||
3690 (0 != aTextStyle.mWordSpacing) ||
3691 aTextStyle.mJustifying);
3692 nsCharType charType = eCharType_LeftToRight;
3694 if (aPresContext->BidiEnabled()) {
3695 isBidiSystem = aPresContext->IsBidiSystem();
3696 nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils();
3698 if (bidiUtils) {
3699 isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
3700 charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsGkAtoms::charType));
3701 #ifdef DEBUG
3702 PRInt32 rememberTextLength = textLength;
3703 #endif
3704 isRightToLeftOnBidiPlatform = (!paintCharByChar &&
3705 isBidiSystem &&
3706 (eCharType_RightToLeft == charType ||
3707 eCharType_RightToLeftArabic == charType));
3708 if (isRightToLeftOnBidiPlatform) {
3709 // indicate that the platform should use its native
3710 // capabilities to reorder the text with right-to-left
3711 // base direction
3712 aRenderingContext.SetRightToLeftText(PR_TRUE);
3714 PRUint32 hints = 0;
3715 aRenderingContext.GetHints(hints);
3716 // If we will be painting char by char, handle the text like on non-bidi platform
3717 bidiUtils->ReorderUnicodeText(text, textLength, charType,
3718 isOddLevel, (paintCharByChar) ? PR_FALSE : isBidiSystem,
3719 (hints & NS_RENDERING_HINT_NEW_TEXT_RUNS) != 0);
3720 NS_ASSERTION(rememberTextLength == textLength, "Bidi formatting changed text length");
3723 #endif // IBMBIDI
3724 ComputeExtraJustificationSpacing(aRenderingContext, aTextStyle, text, textLength, numJustifiableCharacter);
3725 if (!displaySelection || !isSelected) {
3726 // When there is no selection showing, use the fastest and
3727 // simplest rendering approach
3728 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
3729 RenderString(aRenderingContext, aStyleContext, aPresContext, aTextStyle,
3730 PR_FALSE, text, textLength, PR_TRUE, dx, dy, width);
3732 else
3734 SelectionDetails *details = nsnull;
3735 nsCOMPtr<nsIContent> content;
3736 PRInt32 offset;
3737 PRInt32 length;
3738 nsresult rv = GetContentAndOffsetsForSelection(aPresContext,
3739 getter_AddRefs(content),
3740 &offset, &length);
3741 if (NS_SUCCEEDED(rv)) {
3742 details = GetFrameSelection()->LookUpSelection(content, mContentOffset,
3743 mContentLength, PR_FALSE);
3746 //where are the selection points "really"
3747 SelectionDetails *sdptr = details;
3748 while (sdptr){
3749 sdptr->mStart = ip[sdptr->mStart] - mContentOffset;
3750 sdptr->mEnd = ip[sdptr->mEnd] - mContentOffset;
3751 #ifdef IBMBIDI
3752 AdjustSelectionPointsForBidi(sdptr, textLength,
3753 CHARTYPE_IS_RTL(charType), isOddLevel,
3754 (paintCharByChar) ? PR_FALSE : isBidiSystem);
3755 #endif
3756 sdptr = sdptr->mNext;
3759 DrawSelectionIterator iter(details, text, (PRUint32)textLength, &aTextStyle,
3760 nsTextPaintStyle::eAllSelections);
3761 if (!iter.IsDone() && iter.First())
3763 nscoord currentX = dx;
3764 nsTextDimensions newDimensions;//temp
3765 #ifdef IBMBIDI // Simon - display substrings RTL in RTL frame
3766 if (isRightToLeftOnBidiPlatform)
3767 currentX = dx + mRect.width;
3768 #endif
3769 while (!iter.IsDone())
3771 PRUnichar *currenttext = iter.CurrentTextUnicharPtr();
3772 PRUint32 currentlength= iter.CurrentLength();
3773 nscolor currentFGColor, currentBKColor;
3774 PRBool isCurrentBKColorTransparent;
3775 PRBool isSelection = iter.GetSelectionColors(&currentFGColor,
3776 &currentBKColor,
3777 &isCurrentBKColorTransparent);
3778 PRBool isEndOfFrame = iter.IsLast();
3779 GetTextDimensions(aRenderingContext, aTextStyle, currenttext,
3780 (PRInt32)currentlength, isEndOfFrame, &newDimensions);
3781 if (newDimensions.width)
3783 #ifdef IBMBIDI
3784 if (isRightToLeftOnBidiPlatform)
3785 currentX -= newDimensions.width;
3786 #endif
3787 if (isSelection && !isPaginated)
3788 {//DRAW RECT HERE!!!
3789 if (!isCurrentBKColorTransparent) {
3790 aRenderingContext.SetColor(currentBKColor);
3791 aRenderingContext.FillRect(currentX, dy, newDimensions.width, mRect.height);
3796 if (isPaginated && !iter.IsBeforeOrAfter()) {
3797 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor, canDarkenColor));
3798 RenderString(aRenderingContext, aStyleContext, aPresContext,
3799 aTextStyle, isRightToLeftOnBidiPlatform,
3800 currenttext, currentlength, isEndOfFrame,
3801 currentX, dy, newDimensions.width, details);
3802 } else if (!isPaginated) {
3803 aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor, canDarkenColor));
3804 RenderString(aRenderingContext,aStyleContext, aPresContext,
3805 aTextStyle, isRightToLeftOnBidiPlatform,
3806 currenttext, currentlength, isEndOfFrame,
3807 currentX, dy, newDimensions.width, details);
3810 #ifdef IBMBIDI
3811 if (!isRightToLeftOnBidiPlatform)
3812 #endif
3813 // increment twips X start but remember to get ready for
3814 // next draw by reducing current x by letter spacing amount
3815 currentX += newDimensions.width; // + aTextStyle.mLetterSpacing;
3817 iter.Next();
3820 else if (!isPaginated)
3822 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
3823 RenderString(aRenderingContext, aStyleContext, aPresContext,
3824 aTextStyle, isRightToLeftOnBidiPlatform, text,
3825 PRUint32(textLength), PR_TRUE, dx, dy, width, details);
3827 sdptr = details;
3828 if (details){
3829 while ((sdptr = details->mNext) != nsnull) {
3830 delete details;
3831 details = sdptr;
3833 delete details;
3836 #ifdef IBMBIDI
3837 if (isRightToLeftOnBidiPlatform) {
3838 // indicate that future text should not be reordered with
3839 // right-to-left base direction
3840 aRenderingContext.SetRightToLeftText(PR_FALSE);
3842 #endif // IBMBIDI
3846 void
3847 nsTextFrame::PaintAsciiText(nsPresContext* aPresContext,
3848 nsIRenderingContext& aRenderingContext,
3849 nsStyleContext* aStyleContext,
3850 nsTextPaintStyle& aTextStyle,
3851 nscoord dx, nscoord dy)
3853 NS_PRECONDITION(0 == (TEXT_HAS_MULTIBYTE & mState), "text is multi-byte");
3855 nsCOMPtr<nsISelectionController> selCon;
3856 nsCOMPtr<nsIPresShell> shell;
3857 PRBool displaySelection,canDarkenColor=PR_FALSE;
3858 PRBool isPaginated;
3859 PRBool isSelected;
3860 PRBool hideStandardSelection;
3861 PRInt16 selectionValue;
3862 if (NS_FAILED(GetTextInfoForPainting(aPresContext,
3863 getter_AddRefs(shell),
3864 getter_AddRefs(selCon),
3865 displaySelection,
3866 isPaginated,
3867 isSelected,
3868 hideStandardSelection,
3869 selectionValue))) {
3870 return;
3873 if(isPaginated){
3874 canDarkenColor = CanDarken(aPresContext);
3877 // Get the text fragment
3878 const nsTextFragment* frag = mContent->GetText();
3879 if (!frag) {
3880 return;
3883 // Make enough space to transform
3884 nsAutoTextBuffer unicodePaintBuffer;
3885 nsAutoIndexBuffer indexBuffer;
3886 if (displaySelection) {
3887 if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) {
3888 return;
3892 nsTextTransformer tx(aPresContext);
3894 // See if we need to transform the text. If the text fragment is ascii and
3895 // wasn't transformed, then we can skip this step. If we're displaying the
3896 // selection and the text is selected, then we need to do this step so we
3897 // can create the index buffer
3898 PRInt32 textLength = 0;
3899 const char* text;
3900 char paintBufMem[TEXT_BUF_SIZE];
3901 char* paintBuf = paintBufMem;
3902 if (frag->Is2b() ||
3903 (0 != (mState & TEXT_WAS_TRANSFORMED)) ||
3904 (displaySelection && isSelected)) {
3906 // Transform text from content into Unicode renderable form
3907 // XXX If the text fragment is ascii, then we should ask the
3908 // text transformer to leave the text in ascii. That way we can
3909 // elimninate the conversion from Unicode back to ascii...
3910 PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull),
3911 &unicodePaintBuffer, &textLength);
3914 // Translate unicode data into ascii for rendering
3915 if (textLength > TEXT_BUF_SIZE) {
3916 paintBuf = new char[textLength];
3917 if (!paintBuf) {
3918 return;
3921 char* dst = paintBuf;
3922 char* end = dst + textLength;
3923 PRUnichar* src = unicodePaintBuffer.mBuffer;
3924 while (dst < end) {
3925 *dst++ = (char) ((unsigned char) *src++);
3928 text = paintBuf;
3931 else if (mContentOffset + mContentLength <= frag->GetLength()) {
3932 text = frag->Get1b() + mContentOffset;
3933 textLength = mContentLength;
3935 // See if we should skip leading whitespace
3936 if (0 != (mState & TEXT_SKIP_LEADING_WS)) {
3937 while ((textLength > 0) && XP_IS_SPACE(*text)) {
3938 text++;
3939 textLength--;
3943 // See if the text ends in a newline
3944 if ((textLength > 0) && (text[textLength - 1] == '\n')) {
3945 textLength--;
3947 NS_ASSERTION(textLength >= 0, "bad text length");
3949 else {
3950 // This might happen if a paint event beats the reflow; e.g., as
3951 // is the case in bug 73291. Not a big deal, because the reflow
3952 // will schedule another invalidate.
3953 NS_WARNING("content length exceeds fragment length");
3956 nscoord width = mRect.width;
3957 PRInt32* ip = indexBuffer.mBuffer;
3959 if (0 != textLength) {
3960 if (!displaySelection || !isSelected) {
3961 //if selection is > content length then selection has "slid off"
3962 // When there is no selection showing, use the fastest and
3963 // simplest rendering approach
3964 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
3965 aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
3966 PaintTextDecorations(aRenderingContext, aStyleContext,
3967 aPresContext, aTextStyle, dx, dy, width, PR_FALSE);
3969 else {
3970 SelectionDetails *details = nsnull;
3971 nsCOMPtr<nsIContent> content;
3972 PRInt32 offset;
3973 PRInt32 length;
3974 nsresult rv = GetContentAndOffsetsForSelection(aPresContext,
3975 getter_AddRefs(content),
3976 &offset, &length);
3977 if (NS_SUCCEEDED(rv)) {
3978 details = GetFrameSelection()->LookUpSelection(content, mContentOffset,
3979 mContentLength, PR_FALSE);
3982 //where are the selection points "really"
3983 SelectionDetails *sdptr = details;
3984 while (sdptr){
3985 sdptr->mStart = ip[sdptr->mStart] - mContentOffset;
3986 sdptr->mEnd = ip[sdptr->mEnd] - mContentOffset;
3987 sdptr = sdptr->mNext;
3990 if (!hideStandardSelection || displaySelection) {
3991 //ITS OK TO CAST HERE THE RESULT WE USE WILLNOT DO BAD CONVERSION
3992 DrawSelectionIterator iter(details, (PRUnichar *)text,
3993 (PRUint32)textLength, &aTextStyle,
3994 nsTextPaintStyle::eAllSelections);
3996 // See if this rendering backend supports getting cluster
3997 // information.
3998 PRUint32 clusterHint = 0;
3999 aRenderingContext.GetHints(clusterHint);
4000 clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
4002 if (!iter.IsDone() && iter.First())
4004 nscoord currentX = dx;
4005 nscoord newWidth;//temp
4006 while (!iter.IsDone())
4008 char *currenttext = iter.CurrentTextCStrPtr();
4009 PRUint32 currentlength= iter.CurrentLength();
4010 nscolor currentFGColor, currentBKColor;
4011 PRBool isCurrentBKColorTransparent;
4013 if (clusterHint) {
4014 PRUint32 tmpWidth;
4015 rv = aRenderingContext.GetRangeWidth(text, textLength, currenttext - text,
4016 (currenttext - text) + currentlength,
4017 tmpWidth);
4018 newWidth = nscoord(tmpWidth);
4020 else {
4021 rv = aRenderingContext.GetWidth(currenttext, currentlength,newWidth); //ADJUST FOR CHAR SPACING
4024 PRBool isSelection = iter.GetSelectionColors(&currentFGColor,
4025 &currentBKColor,
4026 &isCurrentBKColorTransparent);
4028 if (NS_SUCCEEDED(rv)) {
4029 if (isSelection && !isPaginated)
4030 {//DRAW RECT HERE!!!
4031 if (!isCurrentBKColorTransparent) {
4032 aRenderingContext.SetColor(currentBKColor);
4033 aRenderingContext.FillRect(currentX, dy, newWidth, mRect.height);
4037 else {
4038 newWidth =0;
4041 aRenderingContext.PushState();
4043 nsRect rect(currentX, dy, newWidth, mRect.height);
4044 aRenderingContext.SetClipRect(rect, nsClipCombine_kIntersect);
4046 if (isPaginated && !iter.IsBeforeOrAfter()) {
4047 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
4048 aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
4049 } else if (!isPaginated) {
4050 aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor,canDarkenColor));
4051 aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
4054 aRenderingContext.PopState();
4056 currentX+=newWidth;//increment twips X start
4058 iter.Next();
4061 else if (!isPaginated)
4063 aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor));
4064 aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent);
4068 PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext,
4069 aTextStyle, dx, dy, width, PR_FALSE,
4070 unicodePaintBuffer.mBuffer,
4071 details, 0, textLength);
4072 sdptr = details;
4073 if (details){
4074 while ((sdptr = details->mNext) != nsnull) {
4075 delete details;
4076 details = sdptr;
4078 delete details;
4083 // Cleanup
4084 if (paintBuf != paintBufMem) {
4085 delete [] paintBuf;
4089 // XXX I don't really want to rewrite GetPositionHelper, so I'm doing this
4090 // hack for now
4091 nsIFrame::ContentOffsets nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) {
4092 ContentOffsets offsets;
4093 GetPositionHelper(aPoint, getter_AddRefs(offsets.content), offsets.offset,
4094 offsets.secondaryOffset);
4095 offsets.associateWithNext = (mContentOffset == offsets.offset);
4096 return offsets;
4099 //---------------------------------------------------------------------------
4100 // Uses a binary search to find the position of the cursor in the text.
4101 // The "indices array is used to map from the compressed text back to the
4102 // un-compressed text, selection is based on the un-compressed text, the visual
4103 // display of selection is based on the compressed text.
4104 //---------------------------------------------------------------------------
4105 NS_IMETHODIMP
4106 nsTextFrame::GetPositionHelper(const nsPoint& aPoint,
4107 nsIContent ** aNewContent,
4108 PRInt32& aContentOffset,
4109 PRInt32& aContentOffsetEnd)
4112 // pre-condition tests
4113 NS_PRECONDITION(aNewContent, "null arg");
4114 if (!aNewContent) {
4115 return NS_ERROR_NULL_POINTER;
4117 // initialize out param
4118 *aNewContent = nsnull;
4120 DEBUG_VERIFY_NOT_DIRTY(mState);
4121 if (mState & NS_FRAME_IS_DIRTY)
4122 return NS_ERROR_UNEXPECTED;
4124 nsPresContext *presContext = PresContext();
4125 nsIPresShell *shell = presContext->GetPresShell();
4126 if (shell) {
4127 nsCOMPtr<nsIRenderingContext> rendContext;
4128 nsresult rv = shell->CreateRenderingContext(this, getter_AddRefs(rendContext));
4129 if (NS_SUCCEEDED(rv)) {
4130 nsTextStyle ts(presContext, *rendContext, mStyleContext);
4131 SetupTextRunDirection(presContext, rendContext);
4132 if (ts.mSmallCaps || ts.mWordSpacing || ts.mLetterSpacing || ts.mJustifying) {
4133 nsresult result = GetPositionSlowly(rendContext, aPoint, aNewContent,
4134 aContentOffset);
4135 aContentOffsetEnd = aContentOffset;
4136 return result;
4139 // Make enough space to transform
4140 nsAutoTextBuffer paintBuffer;
4141 nsAutoIndexBuffer indexBuffer;
4142 rv = indexBuffer.GrowTo(mContentLength + 1);
4143 if (NS_FAILED(rv)) {
4144 return rv;
4147 // Find the font metrics for this text
4148 nsLayoutUtils::SetFontFromStyle(rendContext, mStyleContext);
4150 // Get the renderable form of the text
4151 nsTextTransformer tx(PresContext());
4152 PRInt32 textLength;
4153 // no need to worry about justification, that's always on the slow path
4154 PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
4156 if (textLength <= 0) {
4157 aContentOffset = mContentOffset;
4158 aContentOffsetEnd = aContentOffset;
4160 else
4162 PRInt32* ip = indexBuffer.mBuffer;
4164 PRInt32 indx;
4165 PRInt32 textWidth = 0;
4166 PRUnichar* text = paintBuffer.mBuffer;
4168 // See if the font backend will do all the hard work for us.
4169 PRUint32 clusterHint = 0;
4170 rendContext->GetHints(clusterHint);
4171 clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS;
4172 if (clusterHint) {
4173 indx = rendContext->GetPosition(text, textLength, aPoint);
4175 else {
4176 #ifdef IBMBIDI
4177 PRBool getReversedPos = NS_GET_EMBEDDING_LEVEL(this) & 1;
4178 nscoord posX = (getReversedPos) ?
4179 (mRect.width) - (aPoint.x) : aPoint.x;
4181 PRBool found = nsLayoutUtils::BinarySearchForPosition(rendContext, text, 0, 0, 0,
4182 PRInt32(textLength),
4183 PRInt32(posX) , //go to local coordinates
4184 indx, textWidth);
4186 #else
4187 PRBool found = nsLayoutUtils::BinarySearchForPosition(rendContext, text, 0, 0, 0,
4188 PRInt32(textLength),
4189 PRInt32(aPoint.x) , //go to local coordinates
4190 indx, textWidth);
4191 #endif // IBMBIDI
4192 if (found) {
4193 PRInt32 charWidth;
4194 if (NS_IS_HIGH_SURROGATE(text[indx]))
4195 rendContext->GetWidth(&text[indx], 2, charWidth);
4196 else
4197 rendContext->GetWidth(text[indx], charWidth);
4198 charWidth /= 2;
4200 #ifdef IBMBIDI
4201 if (getReversedPos) {
4202 if (mRect.width - aPoint.x> textWidth+charWidth ) {
4203 indx++;
4206 else
4207 #endif // IBMBIDI
4208 if ((aPoint.x) > textWidth+charWidth) {
4209 indx++;
4214 aContentOffset = indx + mContentOffset;
4215 //reusing wordBufMem
4216 PRInt32 i;
4217 for (i = 0; i < mContentLength; i ++){
4218 if ((ip[i] >= aContentOffset) && //reverse mapping
4219 (! NS_IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) {
4220 break;
4223 aContentOffset = i + mContentOffset;
4224 #ifdef IBMBIDI
4225 PRInt32 bidiStopOffset = mContentOffset + mContentLength;
4227 if (aContentOffset >= mContentOffset && aContentOffset < bidiStopOffset) {
4228 PRInt32 curindx = ip[aContentOffset - mContentOffset] - mContentOffset;
4229 while (curindx < textLength && IS_BIDI_DIACRITIC(text[curindx])) {
4230 if (++aContentOffset >= bidiStopOffset)
4231 break;
4232 curindx = ip[aContentOffset - mContentOffset] - mContentOffset;
4235 #endif // IBMBIDI
4236 aContentOffsetEnd = aContentOffset;
4237 NS_ASSERTION(i<= mContentLength, "offset we got from binary search is messed up");
4239 *aNewContent = mContent;
4240 if (*aNewContent) {
4241 (*aNewContent)->AddRef();
4245 return NS_OK;
4248 // [HACK] Foward Declarations
4249 void ForceDrawFrame(nsFrame * aFrame);
4251 //null range means the whole thing
4252 NS_IMETHODIMP
4253 nsTextFrame::SetSelected(nsPresContext* aPresContext,
4254 nsIDOMRange *aRange,
4255 PRBool aSelected,
4256 nsSpread aSpread)
4258 DEBUG_VERIFY_NOT_DIRTY(mState);
4259 #if 0 //XXXrbs disable due to bug 310318
4260 if (mState & NS_FRAME_IS_DIRTY)
4261 return NS_ERROR_UNEXPECTED;
4262 #endif
4264 if (aSelected && ParentDisablesSelection())
4265 return NS_OK;
4267 #if 0
4268 PRBool isSelected = ((GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT);
4269 if (!aSelected && !isSelected) //already set thanks
4271 return NS_OK;
4273 #endif
4275 // check whether style allows selection
4276 PRBool selectable;
4277 IsSelectable(&selectable, nsnull);
4278 if (!selectable)
4279 return NS_OK;//do not continue no selection for this frame.
4281 PRBool found = PR_FALSE;
4282 if (aRange) {
4283 //lets see if the range contains us, if so we must redraw!
4284 nsCOMPtr<nsIDOMNode> endNode;
4285 PRInt32 endOffset;
4286 nsCOMPtr<nsIDOMNode> startNode;
4287 PRInt32 startOffset;
4288 aRange->GetEndContainer(getter_AddRefs(endNode));
4289 aRange->GetEndOffset(&endOffset);
4290 aRange->GetStartContainer(getter_AddRefs(startNode));
4291 aRange->GetStartOffset(&startOffset);
4292 nsCOMPtr<nsIDOMNode> thisNode = do_QueryInterface(GetContent());
4294 if (thisNode == startNode)
4296 if ((mContentOffset + mContentLength) >= startOffset)
4298 found = PR_TRUE;
4299 if (thisNode == endNode)
4300 { //special case
4301 if (endOffset == startOffset) //no need to redraw since drawing takes place with cursor
4302 found = PR_FALSE;
4304 if (mContentOffset > endOffset)
4305 found = PR_FALSE;
4309 else if (thisNode == endNode)
4311 if (mContentOffset < endOffset)
4312 found = PR_TRUE;
4313 else
4315 found = PR_FALSE;
4318 else
4320 found = PR_TRUE;
4323 else {
4324 // null range means the whole thing
4325 found = PR_TRUE;
4328 if ( aSelected )
4329 AddStateBits(NS_FRAME_SELECTED_CONTENT);
4330 else
4331 {//we need to see if any other selection available.
4332 SelectionDetails *details = nsnull;
4333 nsCOMPtr<nsIContent> content;
4334 PRInt32 offset;
4335 PRInt32 length;
4337 nsresult rv = GetContentAndOffsetsForSelection(aPresContext,
4338 getter_AddRefs(content),
4339 &offset, &length);
4340 if (NS_SUCCEEDED(rv) && content) {
4341 details = GetFrameSelection()->LookUpSelection(content, offset,
4342 length, PR_TRUE);
4343 // PR_TRUE last param used here! we need to see if we are still selected. so no shortcut
4345 if (!details)
4346 RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
4347 else
4349 SelectionDetails *sdptr = details;
4350 while ((sdptr = details->mNext) != nsnull) {
4351 delete details;
4352 details = sdptr;
4354 delete details;
4357 if (found){ //if range contains this frame...
4358 // Selection might change our border, content and outline appearance
4359 // But textframes can't have an outline. So just use the simple
4360 // bounds
4361 Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_FALSE);
4363 if (aSpread == eSpreadDown)
4365 nsIFrame* frame = GetPrevContinuation();
4366 while(frame){
4367 frame->SetSelected(aPresContext, aRange,aSelected,eSpreadNone);
4368 frame = frame->GetPrevContinuation();
4370 frame = GetNextContinuation();
4371 while (frame){
4372 frame->SetSelected(aPresContext, aRange,aSelected,eSpreadNone);
4373 frame = frame->GetNextContinuation();
4376 return NS_OK;
4379 NS_IMETHODIMP
4380 nsTextFrame::GetPointFromOffset(nsPresContext* aPresContext,
4381 nsIRenderingContext* inRendContext,
4382 PRInt32 inOffset,
4383 nsPoint* outPoint)
4385 if (!aPresContext || !inRendContext || !outPoint)
4386 return NS_ERROR_NULL_POINTER;
4388 outPoint->x = 0;
4389 outPoint->y = 0;
4391 DEBUG_VERIFY_NOT_DIRTY(mState);
4392 if (mState & NS_FRAME_IS_DIRTY)
4393 return NS_ERROR_UNEXPECTED;
4395 if (mContentLength <= 0) {
4396 return NS_OK;
4399 inOffset-=mContentOffset;
4400 if (inOffset < 0){
4401 NS_ASSERTION(0,"offset less than this frame has in GetPointFromOffset");
4402 inOffset = 0;
4404 if (inOffset >= mContentLength)
4405 inOffset = mContentLength;
4407 nsTextStyle ts(aPresContext, *inRendContext, mStyleContext);
4408 SetupTextRunDirection(aPresContext, inRendContext);
4410 // Make enough space to transform
4411 nsAutoTextBuffer paintBuffer;
4412 nsAutoIndexBuffer indexBuffer;
4413 nsresult rv = indexBuffer.GrowTo(mContentLength + 1);
4414 if (NS_FAILED(rv)) {
4415 return rv;
4418 // Transform text from content into renderable form
4419 nsTextTransformer tx(aPresContext);
4420 PRInt32 textLength;
4421 PRIntn numJustifiableCharacter;
4423 PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength, PR_FALSE, &numJustifiableCharacter);
4425 ComputeExtraJustificationSpacing(*inRendContext, ts, paintBuffer.mBuffer, textLength, numJustifiableCharacter);
4428 PRInt32* ip = indexBuffer.mBuffer;
4429 if (inOffset > mContentLength){
4430 NS_ASSERTION(0, "invalid offset passed to GetPointFromOffset");
4431 inOffset = mContentLength;
4434 while (inOffset >=0 && ip[inOffset] < mContentOffset) //buffer has shrunk
4435 inOffset --;
4436 nscoord width = mRect.width;
4437 if (inOffset <0)
4439 NS_ASSERTION(0, "invalid offset passed to GetPointFromOffset");
4440 inOffset=0;
4441 width = 0;
4443 else
4445 PRInt32 hitLength = ip[inOffset] - mContentOffset;
4446 if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing) || ts.mJustifying)
4448 nsTextDimensions dimensions;
4449 GetTextDimensions(*inRendContext, ts, paintBuffer.mBuffer, hitLength,
4450 textLength == hitLength, &dimensions);
4451 width = dimensions.width;
4453 else
4455 PRInt32 totalLength = mContent->TextLength(); // length up to the last-in-flow frame
4456 if ((hitLength == textLength) && (inOffset == mContentLength) &&
4457 (mContentOffset + mContentLength == totalLength)) {
4458 // no need to re-measure when at the end of the last-in-flow
4460 else
4461 inRendContext->GetWidth(paintBuffer.mBuffer, hitLength, width);
4463 if ((hitLength == textLength && inOffset > 0 && ip[inOffset] == ip[inOffset-1])
4464 && (TEXT_TRIMMED_WS & mState)) {
4466 // Offset must be after a space that has
4467 // been trimmed off the end of the frame.
4468 // Add the width of the trimmed space back
4469 // to the total width, so the caret appears
4470 // in the proper place!
4472 // NOTE: the trailing whitespace includes the word and letter spacing!!
4473 width += ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing;
4476 if (NS_GET_EMBEDDING_LEVEL(this) & 1)
4477 outPoint->x = mRect.width - width;
4478 else
4479 outPoint->x = width;
4480 outPoint->y = 0;
4482 return NS_OK;
4487 NS_IMETHODIMP
4488 nsTextFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset,
4489 PRBool inHint,
4490 PRInt32* outFrameContentOffset,
4491 nsIFrame **outChildFrame)
4493 DEBUG_VERIFY_NOT_DIRTY(mState);
4494 #if 0 //XXXrbs disable due to bug 310227
4495 if (mState & NS_FRAME_IS_DIRTY)
4496 return NS_ERROR_UNEXPECTED;
4497 #endif
4499 if (nsnull == outChildFrame)
4500 return NS_ERROR_NULL_POINTER;
4501 PRInt32 contentOffset = inContentOffset;
4503 if (contentOffset != -1) //-1 signified the end of the current content
4504 contentOffset = inContentOffset - mContentOffset;
4506 if ((contentOffset > mContentLength) || ((contentOffset == mContentLength) && inHint) )
4508 //this is not the frame we are looking for.
4509 nsIFrame* nextContinuation = GetNextContinuation();
4510 if (nextContinuation)
4512 return nextContinuation->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
4514 else {
4515 if (contentOffset != mContentLength) //that condition was only for when there is a choice
4516 return NS_ERROR_FAILURE;
4520 if (inContentOffset < mContentOffset) //could happen with floats!
4522 *outChildFrame = GetPrevInFlow();
4523 if (*outChildFrame)
4524 return (*outChildFrame)->GetChildFrameContainingOffset(inContentOffset, inHint,
4525 outFrameContentOffset,outChildFrame);
4526 else
4527 return NS_OK; //this can't be the right thing to do?
4530 *outFrameContentOffset = contentOffset;
4531 *outChildFrame = this;
4532 return NS_OK;
4535 PRBool
4536 nsTextFrame::PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset)
4538 NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range");
4539 return (!IsEmpty());
4542 PRBool
4543 nsTextFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset)
4545 NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range");
4546 PRInt32 startOffset = *aOffset;
4547 // A negative offset means "end of frame".
4548 if (startOffset < 0)
4549 startOffset = mContentLength;
4551 nsPresContext* presContext = PresContext();
4553 // Transform text from content into renderable form
4554 nsAutoTextBuffer paintBuffer;
4555 nsAutoIndexBuffer indexBuffer;
4556 nsresult rv = indexBuffer.GrowTo(mContentLength + 1);
4557 if (NS_FAILED(rv)) {
4558 return PR_FALSE;
4560 PRInt32 textLength;
4561 nsTextTransformer tx(presContext);
4562 PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
4563 PRInt32* ip = indexBuffer.mBuffer;
4565 PRBool found = PR_TRUE;
4567 PRBool selectable;
4568 PRUint8 selectStyle;
4570 IsSelectable(&selectable, &selectStyle);
4571 if (selectStyle == NS_STYLE_USER_SELECT_ALL)
4572 return PR_FALSE;
4574 if (!aForward) {
4575 *aOffset = 0;
4576 PRInt32 i;
4578 nsAutoPRUint8Buffer clusterBuffer;
4579 rv = FillClusterBuffer(presContext, paintBuffer.mBuffer,
4580 (PRUint32)textLength, clusterBuffer);
4581 NS_ENSURE_SUCCESS(rv, rv);
4583 for (i = startOffset-1; i >= 0; i--){
4584 if ((ip[i] < ip[startOffset]) &&
4585 (clusterBuffer.mBuffer[ip[i] - mContentOffset]) &&
4586 (! NS_IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset])))
4588 *aOffset = i;
4589 break;
4593 #ifdef SUNCTL
4594 static NS_DEFINE_CID(kLECID, NS_ULE_CID);
4596 nsCOMPtr<nsILE> ctlObj;
4597 ctlObj = do_CreateInstance(kLECID, &rv);
4598 if (NS_FAILED(rv)) {
4599 NS_WARNING("Cell based cursor movement will not be supported\n");
4600 ctlObj = nsnull;
4602 else {
4603 PRBool needsCTL = PR_FALSE;
4604 PRInt32 previousOffset;
4606 ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*,
4607 paintBuffer.mBuffer),
4608 mContentOffset + startOffset, -1, &needsCTL);
4610 if (needsCTL) {
4611 ctlObj->PrevCluster(NS_REINTERPRET_CAST(const PRUnichar*,
4612 paintBuffer.mBuffer),
4613 textLength, mContentOffset + startOffset,
4614 &previousOffset);
4615 *aOffset = i = previousOffset - mContentOffset;
4618 #endif /* SUNCTL */
4620 if (i < 0)
4621 found = PR_FALSE;
4623 else // aForward
4625 PRInt32 i;
4626 *aOffset = mContentLength;
4628 nsAutoPRUint8Buffer clusterBuffer;
4629 rv = FillClusterBuffer(presContext, paintBuffer.mBuffer,
4630 (PRUint32)textLength, clusterBuffer);
4631 NS_ENSURE_SUCCESS(rv, rv);
4633 for (i = startOffset; i <= mContentLength; i++) {
4634 if ((ip[i] > ip[startOffset]) &&
4635 ((i == mContentLength) ||
4636 (!NS_IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i] - mContentOffset])) &&
4637 (clusterBuffer.mBuffer[ip[i] - mContentOffset]))) {
4638 *aOffset = i;
4639 break;
4643 #ifdef SUNCTL
4644 static NS_DEFINE_CID(kLECID, NS_ULE_CID);
4646 nsCOMPtr<nsILE> ctlObj;
4647 ctlObj = do_CreateInstance(kLECID, &rv);
4648 if (NS_FAILED(rv)) {
4649 NS_WARNING("Cell based cursor movement will not be supported\n");
4650 ctlObj = nsnull;
4652 else {
4653 PRBool needsCTL = PR_FALSE;
4654 PRInt32 nextOffset;
4656 ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*,
4657 paintBuffer.mBuffer),
4658 mContentOffset + startOffset, 0, &needsCTL);
4660 if (needsCTL) {
4662 ctlObj->NextCluster(NS_REINTERPRET_CAST(const PRUnichar*,
4663 paintBuffer.mBuffer),
4664 textLength, mContentOffset + startOffset,
4665 &nextOffset);
4666 *aOffset = i = nextOffset - mContentOffset;
4669 #endif /* SUNCTL */
4671 if (i > mContentLength)
4672 found = PR_FALSE;
4674 return found;
4677 PRBool
4678 nsTextFrame::PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect,
4679 PRInt32* aOffset, PRBool* aSawBeforeType)
4681 NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range");
4682 PRInt32 startOffset = *aOffset;
4683 if (startOffset < 0)
4684 startOffset = mContentLength;
4686 nsTextTransformer tx(PresContext());
4687 PRBool keepSearching = PR_TRUE; //if you run out of chars before you hit the end of word, maybe next frame has more text to select?
4688 PRBool found = PR_FALSE;
4689 PRBool isWhitespace, wasTransformed;
4690 PRInt32 wordLen, contentLen;
4692 PRBool selectable;
4693 PRUint8 selectStyle;
4694 IsSelectable(&selectable, &selectStyle);
4695 if (selectStyle == NS_STYLE_USER_SELECT_ALL)
4696 found = PR_FALSE;
4697 else
4699 tx.Init(this, mContent, mContentOffset + startOffset);
4700 if (!aForward) {
4701 *aOffset = 0; //initialize
4702 #ifdef IBMBIDI
4703 wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1;
4704 #endif // IBMBIDI
4705 if (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace,
4706 PR_FALSE, aIsKeyboardSelect) &&
4707 contentLen <= startOffset) {
4708 if ((aWordSelectEatSpace ? isWhitespace : !isWhitespace) || !*aSawBeforeType){
4709 *aOffset = startOffset - contentLen;
4710 keepSearching = PR_TRUE;
4711 if (aWordSelectEatSpace ? isWhitespace : !isWhitespace)
4712 *aSawBeforeType = PR_TRUE;
4713 #ifdef IBMBIDI
4714 wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1;
4715 #endif // IBMBIDI
4716 while (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen,
4717 &isWhitespace, PR_FALSE,
4718 aIsKeyboardSelect)){
4719 if (aWordSelectEatSpace ? !isWhitespace : *aSawBeforeType)
4720 break;
4721 if (contentLen >= startOffset)
4722 goto TryNextFrame;
4723 *aOffset -= contentLen;
4724 if (aWordSelectEatSpace ? isWhitespace : !isWhitespace)
4725 *aSawBeforeType = PR_TRUE;
4726 #ifdef IBMBIDI
4727 wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1;
4728 #endif // IBMBIDI
4730 keepSearching = *aOffset <= 0;
4731 if (!keepSearching)
4732 found = PR_TRUE;
4734 else {
4735 *aOffset = mContentLength;
4736 found = PR_TRUE;
4740 else { // aForward
4741 *aOffset = mContentLength; //initialize
4743 #ifdef IBMBIDI
4744 wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1;
4745 #endif // IBMBIDI
4746 if (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aIsKeyboardSelect) &&
4747 (startOffset + contentLen <= mContentLength)) {
4749 if ((aWordSelectEatSpace ? isWhitespace : !isWhitespace) || !*aSawBeforeType) {
4750 *aOffset = startOffset + contentLen;
4751 keepSearching = PR_TRUE;
4752 if (aWordSelectEatSpace ? isWhitespace : !isWhitespace)
4753 *aSawBeforeType = PR_TRUE;
4754 #ifdef IBMBIDI
4755 wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1;
4756 #endif // IBMBIDI
4757 while (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aIsKeyboardSelect))
4759 if (aWordSelectEatSpace ? !isWhitespace : *aSawBeforeType)
4760 break;
4761 if (startOffset + contentLen >= mContentLength)
4762 goto TryNextFrame;
4763 if (aWordSelectEatSpace ? isWhitespace : !isWhitespace)
4764 *aSawBeforeType = PR_TRUE;
4765 *aOffset += contentLen;
4766 #ifdef IBMBIDI
4767 wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1;
4768 #endif // IBMBIDI
4770 keepSearching = *aOffset >= mContentLength;
4771 if (!keepSearching)
4772 found = PR_TRUE;
4774 else
4776 *aOffset = 0;
4777 found = PR_TRUE;
4782 TryNextFrame:
4783 if (!found ||
4784 *aOffset > mContentLength ||
4785 *aOffset < 0)
4787 found = PR_FALSE;
4788 *aOffset = PR_MIN(*aOffset, mContentLength);
4789 *aOffset = PR_MAX(*aOffset, 0);
4791 return found;
4794 NS_IMETHODIMP
4795 nsTextFrame::CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval)
4797 if (!aFinished || !_retval)
4798 return NS_ERROR_NULL_POINTER;
4799 if (*aFinished)
4800 return NS_ERROR_FAILURE; //dont call with finished != false
4801 if (mContentOffset > aEndIndex)
4802 return NS_OK; //reached the end
4803 if (mContentOffset > aStartIndex)
4804 aStartIndex = mContentOffset;
4805 if (aStartIndex >= aEndIndex) //how can it be greater?? check anyway
4806 return NS_OK; //reached the end.
4808 nsresult rv ;
4809 if (aStartIndex < (mContentOffset + mContentLength))
4811 //get the presshell
4812 nsIPresShell *shell = aContext->GetPresShell();
4813 if (!shell)
4814 return NS_ERROR_FAILURE;
4816 //get the document
4817 nsIDocument *doc = shell->GetDocument();
4818 if (!doc)
4819 return NS_ERROR_FAILURE;
4820 //create texttransformer
4821 nsTextTransformer tx(aContext);
4822 //create the buffers
4823 nsAutoTextBuffer paintBuffer;
4824 nsAutoIndexBuffer indexBuffer;
4825 if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1)))
4826 return NS_ERROR_FAILURE;//bail out
4828 PRInt32 textLength;
4829 PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength);
4830 if (textLength)//we have something to measure?
4832 PRInt32 start = PR_MAX(aStartIndex,mContentOffset);
4833 PRInt32 end = PR_MIN(mContentOffset + mContentLength-1, aEndIndex); //base 0 index of array
4834 while (start != end)
4836 if (indexBuffer.mBuffer[start] < indexBuffer.mBuffer[start+1]) //we have a rendered char!
4838 *aFinished = PR_TRUE;//we are done bubble out.
4839 *_retval = PR_TRUE;//hit a drawn char
4840 return NS_OK;
4842 start++;
4844 if (start == aEndIndex)
4846 *aFinished = PR_TRUE;
4850 if (aRecurse) //recurse through the siblings.
4852 nsIFrame *nextInFlow = this;
4853 rv = NS_OK;
4854 while (!aFinished && nextInFlow && NS_SUCCEEDED(rv) && !*_retval) //while we havent found anything visible
4856 nextInFlow = nextInFlow->GetNextInFlow();
4857 if (nextInFlow)
4859 rv = nextInFlow->CheckVisibility(aContext,aStartIndex,aEndIndex,PR_FALSE,aFinished,_retval);
4863 return NS_OK;
4868 NS_IMETHODIMP
4869 nsTextFrame::GetOffsets(PRInt32 &start, PRInt32 &end) const
4871 start = mContentOffset;
4872 end = mContentOffset+mContentLength;
4873 return NS_OK;
4876 #define TEXT_MAX_NUM_SEGMENTS 65
4878 struct SegmentData {
4879 PRUint32 mIsWhitespace : 1;
4880 PRUint32 mContentLen : 31; // content length
4882 PRBool IsWhitespace() {return PRBool(mIsWhitespace);}
4884 // Get the content length. This is a running total of all
4885 // the previous segments as well
4886 PRInt32 ContentLen() {return PRInt32(mContentLen);}
4889 struct TextRun {
4890 // Total number of characters and the accumulated content length
4891 PRInt32 mTotalNumChars, mTotalContentLen;
4893 // Words and whitespace each count as a segment
4894 PRInt32 mNumSegments;
4896 // Possible break points specified as offsets into the buffer
4897 PRInt32 mBreaks[TEXT_MAX_NUM_SEGMENTS];
4899 // Per segment data
4900 SegmentData mSegments[TEXT_MAX_NUM_SEGMENTS];
4902 TextRun()
4904 Reset();
4907 void Reset()
4909 mNumSegments = 0;
4910 mTotalNumChars = 0;
4911 mTotalContentLen = 0;
4914 // Returns PR_TRUE if we're currently buffering text
4915 PRBool IsBuffering()
4917 return mNumSegments > 0;
4920 void AddSegment(PRInt32 aNumChars, PRInt32 aContentLen, PRBool aIsWhitespace)
4922 NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow");
4923 #ifdef IBMBIDI
4924 if (mNumSegments >= TEXT_MAX_NUM_SEGMENTS) {
4925 return;
4927 #endif // IBMBIDI
4928 mTotalNumChars += aNumChars;
4929 mBreaks[mNumSegments] = mTotalNumChars;
4930 mSegments[mNumSegments].mIsWhitespace = aIsWhitespace;
4931 mTotalContentLen += aContentLen;
4932 mSegments[mNumSegments].mContentLen = PRUint32(mTotalContentLen);
4933 mNumSegments++;
4937 PRUint32
4938 nsTextFrame::EstimateNumChars(PRUint32 aAvailableWidth,
4939 PRUint32 aAverageCharWidth)
4941 // Estimate the number of characters that will fit. Use 105% of the available
4942 // width divided by the average character width.
4943 // If mAveCharWidth is zero, we can fit the entire line.
4944 if (aAverageCharWidth == 0) {
4945 return PR_UINT32_MAX;
4948 PRUint32 estimatedNumChars = aAvailableWidth / aAverageCharWidth;
4949 return estimatedNumChars + estimatedNumChars / 20;
4952 // Replaced by precompiled CCMap (see bug 180266). To update the list
4953 // of characters, see one of files included below. As for the way
4954 // the original list of characters was obtained by Frank Tang, see bug 54467.
4955 // Updated to fix the regression (bug 263411). The list contains
4956 // characters of the following Unicode character classes : Ps, Pi, Po, Pf, Pe.
4957 // (ref.: http://www.w3.org/TR/2004/CR-CSS21-20040225/selector.html#first-letter)
4958 // Note that the file does NOT yet include non-BMP characters because
4959 // there's no point including them without fixing the way we identify
4960 // 'first-letter' currently working only with BMP characters.
4961 #include "punct_marks.ccmap"
4962 DEFINE_CCMAP(gPuncCharsCCMap, const);
4964 #define IsPunctuationMark(ch) (CCMAP_HAS_CHAR(gPuncCharsCCMap, ch))
4966 static PRBool CanBreakBetween(nsTextFrame* aBefore,
4967 PRBool aBreakWhitespaceBefore,
4968 nsTextFrame* aAfter,
4969 PRBool aBreakWhitespaceAfter,
4970 PRBool aSkipLeadingWhitespaceAfter,
4971 nsIFrame* aLineContainer)
4973 // This assumes text transforms don't change text breaking properties
4974 const nsTextFragment* fragBefore = aBefore->GetContent()->GetText();
4975 const nsTextFragment* fragAfter = aAfter->GetContent()->GetText();
4976 NS_ASSERTION(fragBefore && fragAfter, "text frames with no text!");
4978 // The end of the before-content
4979 PRInt32 beforeOffset = aBefore->GetContentOffset() + aBefore->GetContentLength();
4980 // The start of the after-content
4981 PRInt32 afterOffset = aAfter->GetContentOffset();
4982 PRInt32 afterLength = fragAfter->GetLength() - afterOffset;
4984 if (beforeOffset <= 0 || afterLength <= 0) {
4985 // This shouldn't really happen, text frames shouldn't map no content
4986 #if 0
4987 NS_WARNING("Textframe maps no content");
4988 #endif
4989 return PR_FALSE;
4992 PRUnichar lastBefore = fragBefore->CharAt(beforeOffset - 1);
4993 PRUnichar firstAfter = fragAfter->CharAt(afterOffset);
4995 // If we're skipping leading whitespace in the after-frame, and we actually
4996 // have leading whitespace in the after-frame, then we can't break before
4997 // it. We will rely on a saved break opportunity from the end of the last frame
4998 // (if any). The problem is that we can't accurately figure out here whether
4999 // a break is allowed.
5000 if (aSkipLeadingWhitespaceAfter && XP_IS_SPACE(firstAfter))
5001 return PR_FALSE;
5003 while (IS_DISCARDED(firstAfter)) {
5004 ++afterOffset;
5005 --afterLength;
5006 if (afterLength == 0) {
5007 // aAfter will be entirely skipped. No breaking allowed here.
5008 return PR_FALSE;
5010 firstAfter = fragAfter->CharAt(afterOffset);
5012 while (IS_DISCARDED(lastBefore)) {
5013 --beforeOffset;
5014 if (beforeOffset == 0) {
5015 // aBefore was entirely skipped. Who knows why it called SetTrailingTextFrame.
5016 NS_WARNING("Before-frame should not have called SetTrailingTextFrame");
5017 return PR_FALSE;
5019 lastBefore = fragBefore->CharAt(beforeOffset - 1);
5022 // If the character before or after the boundary is a breakable whitespace
5023 // character that's not skipped, we're good to break.
5024 if ((XP_IS_SPACE(lastBefore) && aBreakWhitespaceBefore) ||
5025 (XP_IS_SPACE(firstAfter) && aBreakWhitespaceAfter))
5026 return PR_TRUE;
5027 // See nsJISx4051LineBreaker::BreakInBetween ... characters 0-255 don't
5028 // trigger complex line breaking behaviour. This is an approximation since
5029 // currently nsJISx4051LineBreaker can look far along a line, but it'll do
5030 // until the line breaker is sorted out.
5031 if (!fragBefore->Is2b() && !fragAfter->Is2b())
5032 return PR_FALSE;
5034 nsIFrame* f =
5035 nsLayoutUtils::GetClosestCommonAncestorViaPlaceholders(aBefore, aAfter, aLineContainer);
5036 NS_ASSERTION(f, "Frames to check not in the same document???");
5037 // Check if our nearest common ancestor allows wrapping between its children
5038 if (f->GetStyleText()->WhiteSpaceCanWrap())
5039 return PR_FALSE;
5041 nsAutoString beforeStr;
5042 nsAutoString afterStr;
5043 fragBefore->AppendTo(beforeStr, 0, beforeOffset);
5044 fragAfter->AppendTo(afterStr, afterOffset, afterLength);
5046 return nsContentUtils::LineBreaker()->BreakInBetween(
5047 beforeStr.get(), beforeStr.Length(), afterStr.get(), afterStr.Length());
5050 nsReflowStatus
5051 nsTextFrame::MeasureText(nsPresContext* aPresContext,
5052 const nsHTMLReflowState& aReflowState,
5053 nsTextTransformer& aTx,
5054 nsTextStyle& aTs,
5055 TextReflowData& aTextData)
5057 PRBool firstThing = PR_TRUE;
5058 nscoord maxWidth = aReflowState.availableWidth;
5059 nsLineLayout& lineLayout = *aReflowState.mLineLayout;
5060 PRInt32 contentLength = aTx.GetContentLength();
5061 PRInt32 startingOffset = aTextData.mOffset;
5062 PRInt32 column = mColumn;
5063 nscoord prevAscent = 0, prevDescent = 0;
5064 PRInt32 lastWordLen = 0;
5065 PRUnichar* lastWordPtr = nsnull;
5066 PRBool endsInWhitespace = PR_FALSE;
5067 PRBool endsInNewline = PR_FALSE;
5068 PRBool justDidFirstLetter = PR_FALSE;
5069 nsTextDimensions dimensions, lastWordDimensions;
5070 PRBool measureTextRuns = PR_FALSE;
5072 // Check whether we can break between the last text frame (if any) and this one
5073 PRBool trailingTextFrameCanWrap;
5074 nsIFrame* lastTextFrame = lineLayout.GetTrailingTextFrame(&trailingTextFrameCanWrap);
5075 PRBool canBreakBetweenTextFrames = PR_FALSE;
5076 if (lastTextFrame) {
5077 NS_ASSERTION(lastTextFrame->GetType() == nsGkAtoms::textFrame,
5078 "Trailing text frame isn't text!");
5079 canBreakBetweenTextFrames =
5080 CanBreakBetween(NS_STATIC_CAST(nsTextFrame*, lastTextFrame),
5081 trailingTextFrameCanWrap,
5082 this, aTextData.mWrapping, aTextData.mSkipWhitespace,
5083 lineLayout.GetLineContainerFrame());
5086 if (contentLength == 0) {
5087 aTextData.mX = 0;
5088 aTextData.mAscent = 0;
5089 aTextData.mDescent = 0;
5090 return NS_FRAME_COMPLETE;
5092 #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)
5093 // see if we have implementation for GetTextDimensions()
5094 PRUint32 hints = 0;
5095 aReflowState.rendContext->GetHints(hints);
5096 if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
5097 measureTextRuns = !aTs.mPreformatted &&
5098 !aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing &&
5099 aTextData.mWrapping;
5101 // Don't measure text runs with letter spacing active, it doesn't work
5102 // it also doesn't work if we are not word-wrapping (bug 42832)
5103 #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)*/
5104 TextRun textRun;
5105 PRUint32 estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX,
5106 aTs.mAveCharWidth);
5108 #ifdef IBMBIDI
5109 nsTextFrame* nextBidi = nsnull;
5110 PRInt32 start = -1, end;
5112 if (mState & NS_FRAME_IS_BIDI) {
5113 nextBidi = NS_STATIC_CAST(nsTextFrame*, GetLastInFlow()->GetNextContinuation());
5114 if (nextBidi) {
5115 if (mContentLength < 1) {
5116 mContentLength = 1;
5118 nextBidi->GetOffsets(start, end);
5119 if (start <= mContentOffset) {
5120 nextBidi->AdjustOffsetsForBidi(mContentOffset + mContentLength, end);
5122 else {
5123 mContentLength = start - mContentOffset;
5127 #endif //IBMBIDI
5129 aTextData.mX = 0;
5130 if (aTextData.mMeasureText) {
5131 aTs.mNormalFont->GetMaxAscent(aTextData.mAscent);
5132 aTs.mNormalFont->GetMaxDescent(aTextData.mDescent);
5134 PRBool firstWordDone = PR_FALSE;
5135 for (;;) {
5136 #ifdef IBMBIDI
5137 if (nextBidi && (mContentLength <= 0) ) {
5138 if (textRun.IsBuffering()) {
5139 // Measure the remaining text
5140 goto MeasureTextRun;
5142 else {
5143 break;
5146 #endif // IBMBIDI
5147 // Get next word/whitespace from the text
5148 PRBool isWhitespace, wasTransformed;
5149 PRInt32 wordLen, contentLen;
5150 union {
5151 char* bp1;
5152 PRUnichar* bp2;
5154 #ifdef IBMBIDI
5155 wordLen = start;
5156 #endif // IBMBIDI
5158 bp2 = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace,
5159 &wasTransformed, textRun.mNumSegments == 0);
5161 // We need to set aTextData.mCanBreakBefore to true after 1st word. But we can't set
5162 // aTextData.mCanBreakBefore without seeing the 2nd word. That's because this frame
5163 // may only contain part of one word, the other part is in next frame.
5164 // we don't care if first word is whitespace, that will be addressed later.
5165 if (!aTextData.mCanBreakBefore && !firstThing && !isWhitespace) {
5166 firstWordDone = PR_TRUE;
5169 #ifdef IBMBIDI
5170 if (nextBidi) {
5171 mContentLength -= contentLen;
5173 if (mContentLength < 0) {
5174 contentLen += mContentLength;
5175 wordLen = PR_MIN(wordLen, contentLen);
5178 #endif // IBMBIDI
5179 // Remember if the text was transformed
5180 if (wasTransformed) {
5181 mState |= TEXT_WAS_TRANSFORMED;
5184 if (bp2) {
5185 if (firstWordDone) {
5186 // The first word has been processed, and 2nd word is seen
5187 // we can set it be breakable here after.
5188 aTextData.mCanBreakBefore = PR_TRUE;
5190 } else {
5191 if (textRun.IsBuffering()) {
5192 // Measure the remaining text
5193 goto MeasureTextRun;
5195 else {
5196 // Advance the offset in case we just consumed a bunch of
5197 // discarded characters. Otherwise, if this is the first piece
5198 // of content for this frame we will attempt to break-before it.
5199 aTextData.mOffset += contentLen;
5200 break;
5204 lastWordLen = wordLen;
5205 lastWordPtr = bp2;
5206 aTextData.mInWord = PR_FALSE;
5208 // Measure the word/whitespace
5209 PRUnichar firstChar;
5210 if (aTx.TransformedTextIsAscii()) {
5211 firstChar = *bp1;
5212 } else {
5213 firstChar = *bp2;
5215 if (isWhitespace) {
5216 if ('\n' == firstChar) {
5217 // We hit a newline. Stop looping.
5218 NS_ASSERTION(aTs.mPreformatted, "newline w/o ts.mPreformatted");
5219 aTextData.mOffset++;
5220 endsInWhitespace = PR_TRUE;
5221 endsInNewline = PR_TRUE;
5222 break;
5224 if (aTextData.mSkipWhitespace) {
5225 aTextData.mOffset += contentLen;
5226 aTextData.mSkipWhitespace = PR_FALSE; // XXXldb Eh?
5228 if (wasTransformed) {
5229 // As long as there were no discarded characters, then don't consider
5230 // skipped leading whitespace as being transformed
5231 if (wordLen == contentLen) {
5232 mState &= ~TEXT_WAS_TRANSFORMED;
5236 // Only set flag when we actually do skip whitespace
5237 mState |= TEXT_SKIP_LEADING_WS;
5238 continue;
5240 firstThing = PR_FALSE;
5242 // NOTE: Even if the textRun absorbs the whitespace below, we still
5243 // want to remember that we're breakable.
5244 aTextData.mCanBreakBefore = PR_TRUE;
5245 aTextData.mFirstLetterOK = PR_FALSE;
5247 if ('\t' == firstChar) {
5248 // Expand tabs to the proper width
5249 wordLen = 8 - (7 & column);
5250 // Apply word spacing to every space derived from a tab
5251 dimensions.width = (aTs.mSpaceWidth + aTs.mWordSpacing + aTs.mLetterSpacing)*wordLen;
5253 // Because we have to expand the tab when rendering consider that
5254 // a transformation of the text
5255 mState |= TEXT_WAS_TRANSFORMED;
5257 else if (textRun.IsBuffering()) {
5258 // Add a whitespace segment
5259 textRun.AddSegment(wordLen, contentLen, PR_TRUE);
5260 continue;
5262 else {
5263 // Apply word spacing to every space, if there's more than one
5264 dimensions.width = wordLen*(aTs.mWordSpacing + aTs.mLetterSpacing + aTs.mSpaceWidth);// XXX simplistic
5267 //Even if there is not enough space for this "space", we still put it
5268 //here instead of next line
5269 column += wordLen;
5270 endsInWhitespace = PR_TRUE;
5271 aTextData.mOffset += contentLen;
5273 if (aTextData.mMeasureText) {
5274 //if we're wrapping, then don't add the whitespace width to the
5275 // x-offset unless the whitespace will fit within maxWidth.''
5276 if (aTextData.mWrapping) {
5277 if (aTextData.mX + dimensions.width <= maxWidth) {
5278 aTextData.mX += dimensions.width;
5280 else {
5281 // since we didn't add the trailing space width, set this flag so that
5282 // we will not trim this non-existing space
5283 aTextData.mTrailingSpaceTrimmed = PR_TRUE;
5284 // Note: word-spacing or letter-spacing can make the "space" really
5285 // wide. But since this space is left out from our width, linelayout
5286 // may still try to fit something narrower at the end of the line.
5287 // So on return (see below), we flag a soft-break status to ensure
5288 // that linelayout doesn't place something where the "space" should
5289 // be.
5290 break;
5293 else {
5294 //if we're not wrapping, then always advance
5295 // the x-offset regardless of maxWidth
5296 aTextData.mX += dimensions.width;
5298 } //(aTextData.mMeasureText)
5300 else {
5301 firstThing = PR_FALSE;
5303 aTextData.mSkipWhitespace = PR_FALSE;
5305 // XXX :first-letter should be handled during frame construction
5306 // (and it has a good bit in common with nextBidi)
5307 if (aTextData.mFirstLetterOK) {
5308 if (IsPunctuationMark(firstChar)) {
5309 if (contentLen > 1)
5311 wordLen = 2;
5312 contentLen = 2;
5315 else {
5316 wordLen = 1;
5317 contentLen = 1;
5319 justDidFirstLetter = PR_TRUE;
5322 if (aTextData.mMeasureText) {
5323 if (measureTextRuns && !justDidFirstLetter) {
5324 // Add another word to the text run
5325 textRun.AddSegment(wordLen, contentLen, PR_FALSE);
5327 // See if we should measure the text
5328 if ((textRun.mTotalNumChars >= estimatedNumChars) ||
5329 (textRun.mNumSegments >= (TEXT_MAX_NUM_SEGMENTS - 1))) {
5330 goto MeasureTextRun;
5333 else {
5334 if (aTs.mSmallCaps) {
5335 MeasureSmallCapsText(aReflowState.rendContext, aTs, bp2, wordLen, PR_FALSE, &dimensions);
5337 else {
5338 // Measure just the one word
5339 if (aTx.TransformedTextIsAscii()) {
5340 aReflowState.rendContext->GetTextDimensions(bp1, wordLen, dimensions);
5341 } else {
5342 aReflowState.rendContext->GetTextDimensions(bp2, wordLen, dimensions);
5344 #ifdef MOZ_MATHML
5345 // If GetBoundingMetrics is available, use the exact glyph metrics
5346 // for ::first-letter
5347 // XXX remove the #ifdef if GetBoundingMetrics becomes mainstream
5348 if (justDidFirstLetter) {
5349 nsresult res;
5350 nsBoundingMetrics bm;
5351 if (aTx.TransformedTextIsAscii()) {
5352 res = aReflowState.rendContext->GetBoundingMetrics(bp1, wordLen, bm);
5353 } else {
5354 res = aReflowState.rendContext->GetBoundingMetrics(bp2, wordLen, bm);
5356 if (NS_SUCCEEDED(res)) {
5357 aTextData.mAscent = dimensions.ascent = bm.ascent;
5358 aTextData.mDescent = dimensions.descent = bm.descent;
5361 #endif
5362 if (aTs.mLetterSpacing) {
5363 dimensions.width += aTs.mLetterSpacing * wordLen;
5366 if (aTs.mWordSpacing) {
5367 if (aTx.TransformedTextIsAscii()) {
5368 for (char* bp = bp1; bp < bp1 + wordLen; bp++) {
5369 if (*bp == ' ') // || *bp == CH_CJKSP)
5370 dimensions.width += aTs.mWordSpacing;
5372 } else {
5373 for (PRUnichar* bp = bp2; bp < bp2 + wordLen; bp++) {
5374 if (*bp == ' ') // || *bp == CH_CJKSP)
5375 dimensions.width += aTs.mWordSpacing;
5381 lastWordDimensions = dimensions;
5383 PRBool canBreak;
5384 if (0 == aTextData.mX) {
5385 canBreak = canBreakBetweenTextFrames;
5386 // Allow breaking between text frames even if mWrapping is false
5387 // (e.g., we're white-space:pre). If canBreakBetweenTextFrames is
5388 // true, then the previous text frame's mWrapping must have been
5389 // true, and we allow breaking between text frames if at least
5390 // one of them allows breaking.
5391 } else {
5392 canBreak = aTextData.mWrapping;
5394 if (canBreak) {
5395 // Remember that we *could* have broken here, even if we choose not to
5396 PRBool forceBreak =
5397 lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset, PR_TRUE);
5398 // See if there is room for the text
5399 if (forceBreak || (aTextData.mX + dimensions.width > maxWidth)) {
5400 // The text will not fit, or a break was forced.
5401 break;
5404 prevAscent = aTextData.mAscent;
5405 prevDescent = aTextData.mDescent;
5407 aTextData.mX += dimensions.width;
5408 if (aTextData.mAscent < dimensions.ascent) {
5409 aTextData.mAscent = dimensions.ascent;
5411 if (aTextData.mDescent < dimensions.descent) {
5412 aTextData.mDescent = dimensions.descent;
5415 column += wordLen;
5416 endsInWhitespace = PR_FALSE;
5417 aTextData.mOffset += contentLen;
5418 if (justDidFirstLetter) {
5419 // Time to stop
5420 break;
5424 else {
5425 // Remember that we *could* have broken before this chunk of text.
5426 PRBool canBreak;
5427 if (aTextData.mOffset == startingOffset) {
5428 canBreak = canBreakBetweenTextFrames;
5429 } else {
5430 canBreak = aTextData.mWrapping;
5432 if (canBreak) {
5433 #ifdef DEBUG
5434 PRBool forceBreak =
5435 #endif
5436 lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset, PR_TRUE);
5437 NS_ASSERTION(!forceBreak, "If we're supposed to break, we should be "
5438 "really measuring");
5441 // We didn't measure the text, but we need to update our state
5442 column += wordLen;
5443 endsInWhitespace = PR_FALSE;
5444 aTextData.mOffset += contentLen;
5445 if (justDidFirstLetter) {
5446 // Time to stop
5447 break;
5451 continue;
5453 MeasureTextRun:
5454 #if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)
5455 // see if we have implementation for GetTextDimensions()
5456 if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
5457 PRInt32 numCharsFit;
5459 PRInt32 forcedOffset = lineLayout.GetForcedBreakPosition(GetContent());
5460 PRInt32 measureChars = textRun.mTotalNumChars;
5461 if (forcedOffset == -1) {
5462 forcedOffset -= aTextData.mOffset;
5463 #ifdef MOZ_CAIRO_GFX
5464 NS_ASSERTION(forcedOffset >= 0,
5465 "Overshot forced offset, we should have already exited");
5466 #endif
5467 if (forcedOffset >= 0 && forcedOffset < textRun.mTotalNumChars) {
5468 // Only measure up to forcedOffset characters. We still need to measure
5469 // to make sure we get the right text dimensions, even though we know
5470 // where we're going to break. This will reduce the number of chars that
5471 // fit, which we then detect as a required break.
5472 measureChars = forcedOffset;
5476 // These calls can return numCharsFit not positioned at a break in the textRun. Beware.
5477 if (aTx.TransformedTextIsAscii()) {
5478 aReflowState.rendContext->GetTextDimensions((char*)aTx.GetWordBuffer(), measureChars,
5479 maxWidth - aTextData.mX,
5480 textRun.mBreaks, textRun.mNumSegments,
5481 dimensions, numCharsFit, lastWordDimensions);
5482 } else {
5483 aReflowState.rendContext->GetTextDimensions(aTx.GetWordBuffer(), measureChars,
5484 maxWidth - aTextData.mX,
5485 textRun.mBreaks, textRun.mNumSegments,
5486 dimensions, numCharsFit, lastWordDimensions);
5489 // See how much of the text fit
5490 PRBool canBreak;
5491 if (0 == aTextData.mX) {
5492 canBreak = canBreakBetweenTextFrames;
5493 } else {
5494 canBreak = aTextData.mWrapping;
5496 if (canBreak && aTextData.mX + dimensions.width > maxWidth) {
5497 // None of the text fits
5498 #ifdef IBMBIDI
5499 nextBidi = nsnull;
5500 #endif // IBMBIDI
5501 break;
5504 // Find the index of the last segment that fit
5505 PRInt32 lastSegment;
5506 if (numCharsFit >= textRun.mTotalNumChars) { // fast path, normal case
5507 NS_ASSERTION(numCharsFit == textRun.mTotalNumChars, "shouldn't overshoot");
5508 lastSegment = textRun.mNumSegments - 1;
5509 } else {
5510 for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ;
5511 NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment");
5512 // now we have textRun.mBreaks[lastSegment] >= numCharsFit
5513 /* O'Callahan XXX: This snippet together with the snippet below prevents mail from loading
5514 Justification seems to work just fine without these changes.
5515 We get into trouble in a case where lastSegment gets set to -1
5517 if (textRun.mBreaks[lastSegment] > numCharsFit) {
5518 // NOTE: this segment did not actually fit!
5519 lastSegment--;
5524 /* O'Callahan XXX: This snippet together with the snippet above prevents mail from loading
5526 if (lastSegment < 0) {
5527 // no segments fit
5528 break;
5529 } */
5531 aTextData.mX += dimensions.width;
5532 if (aTextData.mAscent < dimensions.ascent) {
5533 aTextData.mAscent = dimensions.ascent;
5535 if (aTextData.mDescent < dimensions.descent) {
5536 aTextData.mDescent = dimensions.descent;
5538 // this is where to backup if line-breaking happens to push the last word
5539 prevAscent = aTextData.mAscent;
5540 prevDescent = aTextData.mDescent;
5541 // we can now consider the last word since we know where to backup
5542 if (aTextData.mAscent < lastWordDimensions.ascent) {
5543 aTextData.mAscent = lastWordDimensions.ascent;
5545 if (aTextData.mDescent < lastWordDimensions.descent) {
5546 aTextData.mDescent = lastWordDimensions.descent;
5549 endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace();
5551 // Save last possible backup position. Whitespace segments are always
5552 // breakable since nsTextTransformer reports preformatted spaces as
5553 // "not whitespace". Note that each segment is either entirely whitespace
5554 // or entirely non-whitespace.
5555 PRInt32 lastWhitespaceSegment =
5556 endsInWhitespace ? lastSegment : lastSegment - 1;
5557 if (lastWhitespaceSegment >= 0) {
5558 lineLayout.NotifyOptionalBreakPosition(GetContent(),
5559 aTextData.mOffset + textRun.mSegments[lastWhitespaceSegment].ContentLen(),
5560 PR_TRUE);
5563 column += numCharsFit;
5564 aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen();
5566 // If all the text didn't fit, then we're done
5567 if (numCharsFit != textRun.mTotalNumChars) {
5568 #ifdef IBMBIDI
5569 nextBidi = nsnull;
5570 #endif // IBMBIDI
5571 break;
5574 #ifdef IBMBIDI
5575 if (nextBidi && (mContentLength <= 0) ) {
5576 break;
5578 #endif // IBMBIDI
5580 if (nsnull == bp2) {
5581 // No more text so we're all finished. Advance the offset in case the last
5582 // call to GetNextWord() discarded characters
5583 aTextData.mOffset += contentLen;
5584 break;
5587 // Reset the number of text run segments
5588 textRun.Reset();
5590 // Estimate the remaining number of characters we think will fit
5591 estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX,
5592 aTs.mAveCharWidth);
5594 #endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */
5598 // If we didn't actually measure any text, then make sure it looks
5599 // like we did
5600 if (!aTextData.mMeasureText) {
5601 aTextData.mAscent = mAscent;
5602 aTextData.mDescent = mRect.height - aTextData.mAscent;
5603 aTextData.mX = mRect.width;
5604 if (mState & TEXT_TRIMMED_WS) {
5605 // Add back in the width of a space since it was trimmed away last time
5606 // NOTE: Trailing whitespace includes word and letter spacing!
5607 aTextData.mX += aTs.mSpaceWidth + aTs.mWordSpacing + aTs.mLetterSpacing;
5611 // Inform line layout of how this piece of text ends in whitespace
5612 // (only text objects do this). Note that if x is zero then this
5613 // text object collapsed into nothingness which means it shouldn't
5614 // effect the current setting of the ends-in-whitespace flag, nor should it
5615 // be setting InWord state, and it should be ignored when subsequent text
5616 // considers whether it can break-before.
5617 lineLayout.SetColumn(column);
5618 lineLayout.SetUnderstandsWhiteSpace(PR_TRUE);
5619 if (0 != aTextData.mX) {
5620 lineLayout.SetTrailingTextFrame(this, aTextData.mWrapping);
5621 lineLayout.SetEndsInWhiteSpace(endsInWhitespace);
5622 lineLayout.SetInWord(!endsInWhitespace);
5623 } else {
5624 // Don't allow subsequent text frame to break-before. All our text is
5625 // being skipped (usually whitespace, could be discarded Unicode control
5626 // characters).
5627 lineLayout.SetTrailingTextFrame(nsnull, PR_FALSE);
5628 lineLayout.SetInWord(PR_FALSE);
5630 if (justDidFirstLetter) {
5631 lineLayout.SetFirstLetterFrame(this);
5632 lineLayout.SetFirstLetterStyleOK(PR_FALSE);
5633 mState |= TEXT_FIRST_LETTER;
5636 // Return our reflow status
5637 nsReflowStatus rs = (aTextData.mOffset == contentLength)
5638 #ifdef IBMBIDI
5639 || (aTextData.mOffset == start)
5640 #endif // IBMBIDI
5641 ? NS_FRAME_COMPLETE
5642 : NS_FRAME_NOT_COMPLETE;
5644 if (canBreakBetweenTextFrames && aTextData.mOffset == startingOffset) {
5645 // Couldn't place any text but we can break between text frames, so do that.
5646 return NS_INLINE_LINE_BREAK_BEFORE();
5649 if (endsInNewline) {
5650 lineLayout.SetLineEndsInBR(PR_TRUE);
5651 return NS_INLINE_LINE_BREAK_AFTER(rs);
5654 if (aTextData.mTrailingSpaceTrimmed && rs == NS_FRAME_COMPLETE) {
5655 // Flag a soft-break that we can check (below) if we come back here
5656 lineLayout.SetLineEndsInSoftBR(PR_TRUE);
5657 } else if (lineLayout.GetLineEndsInSoftBR() && !lineLayout.GetEndsInWhiteSpace()) {
5658 // Break-before a word that follows the soft-break flagged earlier
5659 return NS_INLINE_LINE_BREAK_BEFORE();
5662 if (rs == NS_FRAME_COMPLETE && 0 != aTextData.mX && endsInWhitespace &&
5663 aTextData.mWrapping) {
5664 // Remember the break opportunity at the end of this frame
5665 if (lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset,
5666 aTextData.mX <= maxWidth))
5667 return NS_INLINE_LINE_BREAK_AFTER(rs);
5670 if ((aTextData.mOffset != contentLength) && (aTextData.mOffset == startingOffset)) {
5671 // Break-before a long-word that doesn't fit here
5672 return NS_INLINE_LINE_BREAK_BEFORE();
5675 return rs;
5678 /* virtual */ void
5679 nsTextFrame::MarkIntrinsicWidthsDirty()
5681 // Clear the TEXT_OPTIMIZE_RESIZE for the next time around. It'll get
5682 // reset late in Reflow.
5683 RemoveStateBits(TEXT_OPTIMIZE_RESIZE);
5685 nsFrame::MarkIntrinsicWidthsDirty();
5688 // XXX This should really share more code with the first half of MeasureText.
5689 /* virtual */ void
5690 nsTextFrame::AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
5691 nsIFrame::InlineMinWidthData *aData)
5693 nsresult rv;
5695 nsPresContext *presContext = PresContext();
5696 nsTextStyle ts(presContext, *aRenderingContext, mStyleContext);
5697 SetupTextRunDirection(presContext, aRenderingContext);
5698 if (!ts.mFont->mSize)
5699 // XXX If font size is zero, we still need to figure out whether we've
5700 // got non-whitespace text and whether we end in whitespace.
5701 return;
5703 const nsStyleText *styleText = GetStyleText();
5704 PRBool wrapping = styleText->WhiteSpaceCanWrap();
5705 PRBool wsSignificant = styleText->WhiteSpaceIsSignificant();
5706 PRBool atStart = PR_TRUE;
5707 PRBool forceArabicShaping = (ts.mSmallCaps ||
5708 (0 != ts.mWordSpacing) ||
5709 (0 != ts.mLetterSpacing) ||
5710 ts.mJustifying);
5711 nsTextTransformer tx(presContext);
5712 // Keep the text in ascii if possible. Note that if we're measuring small
5713 // caps text then transform to Unicode because the helper function only
5714 // accepts Unicode text
5715 rv = tx.Init(this, mContent, mContentOffset, forceArabicShaping,
5716 !ts.mSmallCaps);
5717 if (NS_FAILED(rv)) {
5718 NS_NOTREACHED("failure initializing text transformer");
5719 return;
5722 if (aData->trailingTextFrame &&
5723 CanBreakBetween(NS_STATIC_CAST(nsTextFrame*, aData->trailingTextFrame),
5724 aData->trailingTextFrame->
5725 GetStyleText()->WhiteSpaceCanWrap(),
5726 this, wrapping,
5727 aData->skipWhitespace, // XXX ???
5728 nsnull)) // XXX Better to pass real frame
5730 aData->OptionallyBreak(aRenderingContext);
5733 for (;;) {
5734 union {
5735 char* bp1;
5736 PRUnichar* bp2;
5738 PRInt32 wordLen = -1, contentLen;
5739 PRBool isWhitespace, wasTransformed;
5740 // XXX Is !aData->skipWhitespace the right criterion for when the
5741 // text transformer should capitalize the first letter?
5742 bp2 = tx.GetNextWord(!aData->skipWhitespace, &wordLen, &contentLen,
5743 &isWhitespace, &wasTransformed);
5744 if (!bp2)
5745 break;
5746 // XXX Watch mContentLength!
5748 if (isWhitespace) {
5749 PRUnichar firstChar;
5750 if (tx.TransformedTextIsAscii()) {
5751 firstChar = *bp1;
5752 } else {
5753 firstChar = *bp2;
5755 if ('\n' == firstChar) {
5756 aData->ForceBreak(aRenderingContext);
5757 aData->skipWhitespace = PR_TRUE;
5758 aData->trailingWhitespace = 0;
5759 } else if (!aData->skipWhitespace || wsSignificant) {
5760 atStart = PR_FALSE;
5761 nscoord width;
5762 if ('\t' == firstChar) {
5763 // XXX Need to track column!
5764 wordLen = 8;
5765 // Apply word spacing to every space derived from a tab
5766 width =
5767 (ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing)*wordLen;
5768 } else {
5769 // Apply word spacing to every space, if there's more than one
5770 width =
5771 wordLen*(ts.mWordSpacing + ts.mLetterSpacing + ts.mSpaceWidth);// XXX simplistic
5773 aData->currentLine += width;
5774 aData->atStartOfLine = PR_FALSE;
5775 if (wsSignificant) {
5776 aData->trailingWhitespace = 0;
5777 aData->skipWhitespace = PR_FALSE;
5778 } else {
5779 aData->trailingWhitespace += width;
5780 aData->skipWhitespace = PR_TRUE;
5783 if (wrapping) {
5784 aData->OptionallyBreak(aRenderingContext);
5787 } else {
5788 if (!atStart && wrapping) {
5789 aData->OptionallyBreak(aRenderingContext);
5792 atStart = PR_FALSE;
5794 nscoord width;
5795 if (ts.mSmallCaps) {
5796 nsTextDimensions dimensions;
5797 // MeasureSmallCapsText measures one character at a time so it *should*
5798 // be OK to just say "LTR" here without breaking things (any more than
5799 // they're already broken)
5800 aRenderingContext->SetTextRunRTL(PR_FALSE);
5801 MeasureSmallCapsText(aRenderingContext, ts, bp2, wordLen, PR_FALSE,
5802 &dimensions);
5803 width = dimensions.width;
5804 } else {
5805 // Unfortunately we might have mixed-directionality text at this point,
5806 // and we might not know our embedding level. So we have to make some
5807 // approximations, until bidi resolution is moved to frame construction.
5808 if (tx.TransformedTextIsAscii()) {
5809 // It may not actually be LTR, but hopefully the width doesn't care
5810 aRenderingContext->SetTextRunRTL(PR_FALSE);
5811 aRenderingContext->GetWidth(bp1, wordLen, width);
5812 } else {
5813 // We may get the directions reversed but at least we'll be breaking
5814 // the string up and measuring segments in the same direction
5815 width =
5816 nsLayoutUtils::GetStringWidth(this, aRenderingContext, bp2, wordLen);
5818 width += ts.mLetterSpacing * wordLen;
5821 aData->currentLine += width;
5822 aData->atStartOfLine = PR_FALSE;
5823 aData->skipWhitespace = PR_FALSE;
5824 aData->trailingWhitespace = 0;
5828 aData->trailingTextFrame = this;
5831 /* virtual */ void
5832 nsTextFrame::AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
5833 nsIFrame::InlinePrefWidthData *aData)
5835 nsresult rv;
5837 nsPresContext *presContext = PresContext();
5838 nsTextStyle ts(presContext, *aRenderingContext, mStyleContext);
5839 if (!ts.mFont->mSize)
5840 // XXX If font size is zero, we still need to figure out whether we've
5841 // got non-whitespace text and whether we end in whitespace.
5842 return;
5844 PRBool forceArabicShaping = (ts.mSmallCaps ||
5845 (0 != ts.mWordSpacing) ||
5846 (0 != ts.mLetterSpacing) ||
5847 ts.mJustifying);
5848 nsTextTransformer tx(presContext);
5849 // Keep the text in ascii if possible. Note that if we're measuring small
5850 // caps text then transform to Unicode because the helper function only
5851 // accepts Unicode text
5852 rv = tx.Init(this, mContent, mContentOffset, forceArabicShaping,
5853 !ts.mSmallCaps);
5854 if (NS_FAILED(rv)) {
5855 NS_NOTREACHED("failure initializing text transformer");
5856 return;
5859 for (;;) {
5860 union {
5861 char* bp1;
5862 PRUnichar* bp2;
5864 PRInt32 wordLen = -1, contentLen;
5865 PRBool isWhitespace, wasTransformed;
5866 // XXX Should fix this to use something better than GetNextWord!
5867 // XXX Is !aData->skipWhitespace the right criterion for when the
5868 // text transformer should capitalize the first letter?
5869 bp2 = tx.GetNextWord(!aData->skipWhitespace, &wordLen, &contentLen,
5870 &isWhitespace, &wasTransformed);
5871 if (!bp2)
5872 break;
5873 // XXX Watch mContentLength!
5875 if (isWhitespace) {
5876 PRUnichar firstChar;
5877 if (tx.TransformedTextIsAscii()) {
5878 firstChar = *bp1;
5879 } else {
5880 firstChar = *bp2;
5882 if ('\n' == firstChar) {
5883 aData->ForceBreak(aRenderingContext);
5884 } else if (!aData->skipWhitespace) {
5885 nscoord width;
5886 if ('\t' == firstChar) {
5887 // XXX Need to track column!
5888 wordLen = 8;
5889 // Apply word spacing to every space derived from a tab
5890 width =
5891 (ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing)*wordLen;
5892 } else {
5893 // Apply word spacing to every space, if there's more than one
5894 width =
5895 wordLen*(ts.mWordSpacing + ts.mLetterSpacing + ts.mSpaceWidth);// XXX simplistic
5897 aData->currentLine += width;
5898 if (GetStyleText()->WhiteSpaceIsSignificant())
5899 // XXX Should we also subtract the old value of
5900 // trailingWhitespace from currentLine?
5901 aData->trailingWhitespace = 0;
5902 else
5903 aData->trailingWhitespace += width;
5905 } else {
5906 nscoord width;
5907 if (ts.mSmallCaps) {
5908 nsTextDimensions dimensions;
5909 // MeasureSmallCapsText measures one character at a time so it *should*
5910 // be OK to just say "LTR" here without breaking things (any more than
5911 // they're already broken)
5912 aRenderingContext->SetTextRunRTL(PR_FALSE);
5913 MeasureSmallCapsText(aRenderingContext, ts, bp2, wordLen, PR_FALSE,
5914 &dimensions);
5915 width = dimensions.width;
5916 } else {
5917 // Unfortunately we might have mixed-directionality text at this point,
5918 // because we may not have reflowed yet, and we might not know our
5919 // embedding level. So we have to make some approximations, until
5920 // bidi resolution is moved to frame construction.
5921 if (tx.TransformedTextIsAscii()) {
5922 // It may not actually be LTR, but hopefully the width doesn't care
5923 aRenderingContext->SetTextRunRTL(PR_FALSE);
5924 aRenderingContext->GetWidth(bp1, wordLen, width);
5925 } else {
5926 // We may get the directions reversed but at least we'll be breaking
5927 // the string up and measuring segments in the same direction
5928 width =
5929 nsLayoutUtils::GetStringWidth(this, aRenderingContext, bp2, wordLen);
5931 width += ts.mLetterSpacing * wordLen;
5934 aData->currentLine += width;
5935 aData->skipWhitespace = PR_FALSE;
5936 aData->trailingWhitespace = 0;
5941 /* virtual */ nsSize
5942 nsTextFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
5943 nsSize aCBSize, nscoord aAvailableWidth,
5944 nsSize aMargin, nsSize aBorder, nsSize aPadding,
5945 PRBool aShrinkWrap)
5947 // Inlines and text don't compute size before reflow.
5948 return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
5951 NS_IMETHODIMP
5952 nsTextFrame::Reflow(nsPresContext* aPresContext,
5953 nsHTMLReflowMetrics& aMetrics,
5954 const nsHTMLReflowState& aReflowState,
5955 nsReflowStatus& aStatus)
5957 DO_GLOBAL_REFLOW_COUNT("nsTextFrame");
5958 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
5959 #ifdef NOISY_REFLOW
5960 ListTag(stdout);
5961 printf(": BeginReflow: availableSize=%d,%d\n",
5962 aReflowState.availableWidth, aReflowState.availableHeight);
5963 #endif
5965 mState &= ~TEXT_IS_END_OF_LINE;
5967 // XXX If there's no line layout, we shouldn't even have created this
5968 // frame. This may happen if, for example, this is text inside a table
5969 // but not inside a cell. For now, just don't reflow.
5970 if (nsnull == aReflowState.mLineLayout) {
5971 // XXX Add a method to aMetrics that does this; we do it several places
5972 aMetrics.width = 0;
5973 aMetrics.height = 0;
5974 aMetrics.ascent = 0;
5975 #ifdef MOZ_MATHML
5976 if (NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags)
5977 aMetrics.mBoundingMetrics.Clear();
5978 #endif
5979 return NS_OK;
5982 // Get starting offset into the content
5983 PRInt32 startingOffset = 0;
5984 nsIFrame* prevInFlow = GetPrevInFlow();
5985 if (nsnull != prevInFlow) {
5986 nsTextFrame* prev = NS_STATIC_CAST(nsTextFrame*, prevInFlow);
5987 startingOffset = prev->mContentOffset + prev->mContentLength;
5989 // If our starting offset doesn't agree with mContentOffset, then our
5990 // prev-in-flow has changed the number of characters it maps and so we
5991 // need to measure text and not try and optimize a resize reflow
5992 if (startingOffset != mContentOffset) {
5993 mState &= ~TEXT_OPTIMIZE_RESIZE;
5996 nsLineLayout& lineLayout = *aReflowState.mLineLayout;
5997 nsTextStyle ts(aPresContext, *aReflowState.rendContext, mStyleContext);
5998 SetupTextRunDirection(aPresContext, aReflowState.rendContext);
6000 if ( (mContentLength > 0) && (mState & NS_FRAME_IS_BIDI) ) {
6001 startingOffset = mContentOffset;
6004 if (aPresContext->BidiEnabled()) {
6005 nsCharType charType = eCharType_LeftToRight;
6006 PRUint32 hints = 0;
6007 aReflowState.rendContext->GetHints(hints);
6008 charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsGkAtoms::charType));
6009 if ((eCharType_RightToLeftArabic == charType &&
6010 (hints & NS_RENDERING_HINT_ARABIC_SHAPING) == NS_RENDERING_HINT_ARABIC_SHAPING) ||
6011 (eCharType_RightToLeft == charType &&
6012 (hints & NS_RENDERING_HINT_BIDI_REORDERING) == NS_RENDERING_HINT_BIDI_REORDERING)) {
6013 // XXXldb This needs to happen before |Reflow|.
6014 aPresContext->SetIsBidiSystem(PR_TRUE);
6018 // Clear out the reflow state flags in mState (without destroying
6019 // the TEXT_BLINK_ON bit).
6020 PRBool lastTimeWeSkippedLeadingWS = 0 != (mState & TEXT_SKIP_LEADING_WS);
6021 mState &= ~TEXT_REFLOW_FLAGS;
6022 if (aReflowState.mFlags.mBlinks) {
6023 if (0 == (mState & TEXT_BLINK_ON)) {
6024 mState |= TEXT_BLINK_ON;
6025 nsBlinkTimer::AddBlinkFrame(aPresContext, this);
6028 else {
6029 if (0 != (mState & TEXT_BLINK_ON)) {
6030 mState &= ~TEXT_BLINK_ON;
6031 nsBlinkTimer::RemoveBlinkFrame(this);
6035 PRBool wrapping = ts.mText->WhiteSpaceCanWrap();
6037 // Set whitespace skip flag
6038 PRBool skipWhitespace = PR_FALSE;
6039 if (!ts.mPreformatted) {
6040 if (lineLayout.GetEndsInWhiteSpace()) {
6041 skipWhitespace = PR_TRUE;
6045 nscoord maxWidth = aReflowState.availableWidth;
6047 // Setup text transformer to transform this frames text content
6048 nsIDocument* doc = mContent->GetDocument();
6049 if (!doc) {
6050 NS_WARNING("Content has no document.");
6051 return NS_ERROR_FAILURE;
6053 PRBool forceArabicShaping = (ts.mSmallCaps ||
6054 (0 != ts.mWordSpacing) ||
6055 (0 != ts.mLetterSpacing) ||
6056 ts.mJustifying);
6057 nsTextTransformer tx(aPresContext);
6058 // Keep the text in ascii if possible. Note that if we're measuring small
6059 // caps text then transform to Unicode because the helper function only
6060 // accepts Unicode text
6061 nsresult rv = tx.Init(this, mContent, startingOffset, forceArabicShaping, !ts.mSmallCaps);
6062 if (NS_OK != rv) {
6063 return rv;
6065 //PRInt32 contentLength = tx.GetContentLength();
6067 // Set inWord to true if we are part of a previous piece of text's word. This
6068 // is only valid for one pass through the measuring loop.
6069 PRBool inWord = lineLayout.InWord();
6070 if (inWord) {
6071 mState |= TEXT_IN_WORD;
6073 mState &= ~TEXT_FIRST_LETTER;
6075 PRInt32 column = lineLayout.GetColumn();
6076 PRInt32 prevColumn = mColumn;
6077 mColumn = column;
6078 PRBool measureText = PR_TRUE;
6080 // We can avoid actually measuring the text if:
6081 // - intrinsic widths haven't been marked dirty (which clears
6082 // TEXT_OPTIMIZE_RESIZE)
6083 // - we don't have a next in flow
6084 // - the previous reflow successfully reflowed all text in the
6085 // available space
6086 // - we aren't computing the max element size (that requires we measure
6087 // text)
6088 // - skipping leading whitespace is the same as it was the last time
6089 // - we're wrapping text and the available width is at least as big as our
6090 // current frame width -or-
6091 // we're not wrapping text and we're at the same column as before (this is
6092 // an issue for preformatted tabbed text only)
6093 // - AND we aren't justified (in which case the frame width has already been tweaked and can't be used)
6094 nscoord realWidth = mRect.width;
6095 if (mState & TEXT_TRIMMED_WS) {
6096 // NOTE: Trailing whitespace includes word and letter spacing!
6097 realWidth += ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing;
6099 if (!GetNextInFlow() &&
6100 (mState & TEXT_OPTIMIZE_RESIZE) &&
6101 lineLayout.GetForcedBreakPosition(GetContent()) == -1 &&
6102 (lastTimeWeSkippedLeadingWS == skipWhitespace) &&
6103 ((wrapping && (maxWidth >= realWidth)) ||
6104 (!wrapping && (prevColumn == column))) &&
6105 #ifdef IBMBIDI
6106 (0 == (mState & NS_FRAME_IS_BIDI) ) &&
6107 #endif // IBMBIDI
6108 !ts.mJustifying) {
6109 // We can skip measuring of text and use the value from our
6110 // previous reflow
6111 measureText = PR_FALSE;
6112 #ifdef NOISY_REFLOW
6113 printf(" => measureText=%s wrapping=%s skipWhitespace=%s",
6114 measureText ? "yes" : "no",
6115 wrapping ? "yes" : "no",
6116 skipWhitespace ? "yes" : "no");
6117 printf(" realWidth=%d maxWidth=%d\n",
6118 realWidth, maxWidth);
6119 #endif
6122 // Local state passed to the routines that do the actual text measurement
6123 TextReflowData textData(startingOffset, wrapping, skipWhitespace,
6124 measureText, inWord, lineLayout.GetFirstLetterStyleOK(),
6125 lineLayout.LineIsBreakable(), PR_FALSE);
6127 // Measure the text
6128 // MeasureText may set TEXT_TRIMMED_WS flag, so don't clear after the call
6129 if (ts.mFont->mSize)
6130 aStatus = MeasureText(aPresContext, aReflowState, tx, ts, textData);
6131 else {
6132 textData.mX = 0;
6133 textData.mAscent = 0;
6134 textData.mDescent = 0;
6135 aStatus = NS_FRAME_COMPLETE;
6137 if (textData.mTrailingSpaceTrimmed)
6138 mState |= TEXT_TRIMMED_WS;
6139 else
6140 mState &= ~TEXT_TRIMMED_WS;
6142 if (tx.HasMultibyte()) {
6143 mState |= TEXT_HAS_MULTIBYTE;
6146 // Setup metrics for caller; store final max-element-size information
6147 aMetrics.width = textData.mX;
6148 if ((0 == textData.mX) && !ts.mPreformatted) {
6149 aMetrics.height = 0;
6150 aMetrics.ascent = 0;
6152 else {
6153 aMetrics.ascent = textData.mAscent;
6154 aMetrics.height = textData.mAscent + textData.mDescent;
6156 mAscent = aMetrics.ascent;
6158 // Set content offset and length
6159 mContentOffset = startingOffset;
6160 mContentLength = textData.mOffset - startingOffset;
6162 // Compute space and letter counts for justification, if required
6163 // Also use this one-shot path to compute the metrics needed for MathML, if required
6164 // (the flag is set only if this text happens to be inside MathML)
6165 PRBool calcMathMLMetrics = PR_FALSE;
6166 nsAutoTextBuffer* textBufferPtr = nsnull;
6167 #ifdef MOZ_MATHML
6168 nsAutoTextBuffer textBuffer;
6169 calcMathMLMetrics = (NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags) != 0;
6170 if (calcMathMLMetrics) {
6171 textBufferPtr = &textBuffer;
6172 // always use the Unicode path with MathML fonts in gfx, it is safer this way
6173 mState |= TEXT_HAS_MULTIBYTE;
6175 #endif
6176 if (ts.mJustifying || calcMathMLMetrics) {
6177 PRIntn numJustifiableCharacter;
6178 PRInt32 textLength;
6180 // This will include a space for trailing whitespace, if any is present.
6181 // This is corrected for in nsLineLayout::TrimWhiteSpaceIn.
6183 // This work could be done in MeasureText, but it's complex to do accurately
6184 // there because of the need to repair counts when wrapped words are backed out.
6185 // So I do it via PrepareUnicodeText ... a little slower perhaps, but a lot saner,
6186 // and it localizes the counting logic to one place.
6187 PrepareUnicodeText(tx, nsnull, textBufferPtr, &textLength, PR_TRUE, &numJustifiableCharacter);
6188 lineLayout.SetTextJustificationWeights(numJustifiableCharacter, textLength - numJustifiableCharacter);
6190 #ifdef MOZ_MATHML
6191 if (calcMathMLMetrics) {
6192 nsLayoutUtils::SetFontFromStyle(aReflowState.rendContext, mStyleContext);
6193 nsBoundingMetrics bm;
6194 rv = aReflowState.rendContext->GetBoundingMetrics(textBuffer.mBuffer, textLength, bm);
6195 if (NS_SUCCEEDED(rv))
6196 aMetrics.mBoundingMetrics = bm;
6197 else {
6198 // Things didn't turn out well, just return the reflow metrics.
6199 aMetrics.mBoundingMetrics.ascent = aMetrics.ascent;
6200 aMetrics.mBoundingMetrics.descent = aMetrics.height - aMetrics.ascent;
6201 aMetrics.mBoundingMetrics.width = aMetrics.width;
6202 aMetrics.mBoundingMetrics.rightBearing = aMetrics.width;
6205 #endif
6208 nscoord maxFrameWidth = mRect.width;
6209 nscoord maxFrameHeight = mRect.height;
6211 // For future resize reflows we would like to avoid measuring the text.
6212 // We can only do this if after this reflow we're:
6213 // - complete. If we're not complete then our desired width doesn't
6214 // represent our total size
6215 // - we fit in the available space. We may be complete, but if we
6216 // return a larger desired width than is available we may get pushed
6217 // and our frame width won't get set
6218 if (NS_FRAME_IS_COMPLETE(aStatus) && !NS_INLINE_IS_BREAK(aStatus) &&
6219 (aMetrics.width <= maxWidth)) {
6220 mState |= TEXT_OPTIMIZE_RESIZE;
6221 mRect.width = aMetrics.width;
6223 else {
6224 mState &= ~TEXT_OPTIMIZE_RESIZE;
6227 // If it's an incremental reflow command, then invalidate our existing
6228 // bounds.
6229 // XXX We need a finer granularity than this, but it isn't clear what
6230 // has actually changed...
6231 /*if (eReflowReason_Incremental == aReflowState.reason ||
6232 eReflowReason_Dirty == aReflowState.reason) {*/
6233 // XXX See bug 71523 We should really adjust the frames x coordinate to
6234 // a pixel boundary to solve this.
6235 // For now we add 1 pixel to the width of the invalidated rect.
6236 // This fixes cases where the twips to pixel roundoff causes the invalidated
6237 // rect's width to be one pixel short.
6238 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
6240 maxFrameWidth = PR_MAX(maxFrameWidth, mRect.width) + onePixel;
6241 maxFrameHeight = PR_MAX(maxFrameHeight, mRect.height);
6242 nsRect damage(0,0,maxFrameWidth,maxFrameHeight);
6243 Invalidate(damage);
6244 /*}*/
6247 #ifdef NOISY_REFLOW
6248 ListTag(stdout);
6249 printf(": desiredSize=%d,%d(b=%d) status=%x\n",
6250 aMetrics.width, aMetrics.height, aMetrics.ascent,
6251 aStatus);
6252 #endif
6253 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
6254 return NS_OK;
6257 /* virtual */ PRBool
6258 nsTextFrame::CanContinueTextRun() const
6260 // We can continue a text run through a text frame
6261 return PR_TRUE;
6264 NS_IMETHODIMP
6265 nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
6266 nsIRenderingContext& aRC,
6267 nscoord& aDeltaWidth,
6268 PRBool& aLastCharIsJustifiable)
6270 aLastCharIsJustifiable = PR_FALSE;
6271 mState |= TEXT_IS_END_OF_LINE;
6273 // in some situation (for instance, in wrapping mode, last space will not
6274 // be added to total width if it exceed maxwidth), this flag will be set
6275 // and we shouldn't trim non-added space
6276 if (mState & TEXT_TRIMMED_WS) {
6277 aDeltaWidth = 0;
6278 return NS_OK;
6281 nscoord dw = 0;
6282 const nsStyleText* textStyle = GetStyleText();
6283 if (mContentLength &&
6284 (NS_STYLE_WHITESPACE_PRE != textStyle->mWhiteSpace) &&
6285 (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP != textStyle->mWhiteSpace)) {
6287 // Get the text fragments that make up our content
6288 const nsTextFragment* frag = mContent->GetText();
6289 if (frag) {
6290 PRInt32 lastCharIndex = mContentOffset + mContentLength - 1;
6291 if (lastCharIndex < frag->GetLength()) {
6292 PRUnichar ch = frag->CharAt(lastCharIndex);
6293 if (XP_IS_SPACE(ch)) {
6294 // Get font metrics for a space so we can adjust the width by the
6295 // right amount.
6296 nsLayoutUtils::SetFontFromStyle(&aRC, mStyleContext);
6298 aRC.GetWidth(' ', dw);
6299 // NOTE: Trailing whitespace includes word and letter spacing!
6300 nsStyleUnit unit;
6301 unit = textStyle->mWordSpacing.GetUnit();
6302 if (eStyleUnit_Coord == unit) {
6303 dw += textStyle->mWordSpacing.GetCoordValue();
6305 unit = textStyle->mLetterSpacing.GetUnit();
6306 if (eStyleUnit_Coord == unit) {
6307 dw += textStyle->mLetterSpacing.GetCoordValue();
6309 aLastCharIsJustifiable = PR_TRUE;
6310 } else if (IsJustifiableCharacter(ch, IsChineseJapaneseLangGroup())) {
6311 aLastCharIsJustifiable = PR_TRUE;
6316 #ifdef NOISY_TRIM
6317 ListTag(stdout);
6318 printf(": trim => %d\n", dw);
6319 #endif
6320 if (0 != dw) {
6321 mState |= TEXT_TRIMMED_WS;
6323 else {
6324 mState &= ~TEXT_TRIMMED_WS;
6326 aDeltaWidth = dw;
6327 return NS_OK;
6330 #ifdef DEBUG
6331 // Translate the mapped content into a string that's printable
6332 void
6333 nsTextFrame::ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const
6335 // Get the frames text content
6336 const nsTextFragment* frag = mContent->GetText();
6337 if (!frag) {
6338 return;
6341 // Compute the total length of the text content.
6342 *aTotalContentLength = frag->GetLength();
6344 // Set current fragment and current fragment offset
6345 if (0 == mContentLength) {
6346 return;
6348 PRInt32 fragOffset = mContentOffset;
6349 PRInt32 n = fragOffset + mContentLength;
6350 while (fragOffset < n) {
6351 PRUnichar ch = frag->CharAt(fragOffset++);
6352 if (ch == '\r') {
6353 aBuf.AppendLiteral("\\r");
6354 } else if (ch == '\n') {
6355 aBuf.AppendLiteral("\\n");
6356 } else if (ch == '\t') {
6357 aBuf.AppendLiteral("\\t");
6358 } else if ((ch < ' ') || (ch >= 127)) {
6359 aBuf.AppendLiteral("\\0");
6360 aBuf.AppendInt((PRInt32)ch, 8);
6361 } else {
6362 aBuf.Append(ch);
6366 #endif
6368 nsIAtom*
6369 nsTextFrame::GetType() const
6371 return nsGkAtoms::textFrame;
6374 /* virtual */ PRBool
6375 nsTextFrame::IsEmpty()
6377 NS_ASSERTION(!(mState & TEXT_IS_ONLY_WHITESPACE) ||
6378 !(mState & TEXT_ISNOT_ONLY_WHITESPACE),
6379 "Invalid state");
6381 // XXXldb Should this check compatibility mode as well???
6382 if (GetStyleText()->WhiteSpaceIsSignificant()) {
6383 return PR_FALSE;
6386 if (mState & TEXT_ISNOT_ONLY_WHITESPACE) {
6387 return PR_FALSE;
6390 if (mState & TEXT_IS_ONLY_WHITESPACE) {
6391 return PR_TRUE;
6394 PRBool isEmpty = mContent->TextIsOnlyWhitespace();
6395 mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
6396 return isEmpty;
6399 #ifdef DEBUG
6400 NS_IMETHODIMP
6401 nsTextFrame::GetFrameName(nsAString& aResult) const
6403 return MakeFrameName(NS_LITERAL_STRING("Text"), aResult);
6406 NS_IMETHODIMP_(nsFrameState)
6407 nsTextFrame::GetDebugStateBits() const
6409 // mask out our emptystate flags; those are just caches
6410 return nsFrame::GetDebugStateBits() &
6411 ~(TEXT_WHITESPACE_FLAGS | TEXT_REFLOW_FLAGS);
6414 NS_IMETHODIMP
6415 nsTextFrame::List(FILE* out, PRInt32 aIndent) const
6417 // Output the tag
6418 IndentBy(out, aIndent);
6419 ListTag(out);
6420 #ifdef DEBUG_waterson
6421 fprintf(out, " [parent=%p]", mParent);
6422 #endif
6423 if (HasView()) {
6424 fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, GetView()));
6427 PRInt32 totalContentLength;
6428 nsAutoString tmp;
6429 ToCString(tmp, &totalContentLength);
6431 // Output the first/last content offset and prev/next in flow info
6432 PRBool isComplete = (mContentOffset + mContentLength) == totalContentLength;
6433 fprintf(out, "[%d,%d,%c] ",
6434 mContentOffset, mContentLength,
6435 isComplete ? 'T':'F');
6437 if (nsnull != mNextSibling) {
6438 fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling));
6440 nsIFrame* prevContinuation = GetPrevContinuation();
6441 if (nsnull != prevContinuation) {
6442 fprintf(out, " prev-continuation=%p", NS_STATIC_CAST(void*, prevContinuation));
6444 if (nsnull != mNextContinuation) {
6445 fprintf(out, " next-continuation=%p", NS_STATIC_CAST(void*, mNextContinuation));
6448 // Output the rect and state
6449 fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
6450 if (0 != mState) {
6451 if (mState & NS_FRAME_SELECTED_CONTENT) {
6452 fprintf(out, " [state=%08x] SELECTED", mState);
6453 } else {
6454 fprintf(out, " [state=%08x]", mState);
6457 fprintf(out, " [content=%p]", NS_STATIC_CAST(void*, mContent));
6458 fprintf(out, " sc=%p", NS_STATIC_CAST(void*, mStyleContext));
6459 nsIAtom* pseudoTag = mStyleContext->GetPseudoType();
6460 if (pseudoTag) {
6461 nsAutoString atomString;
6462 pseudoTag->ToString(atomString);
6463 fprintf(out, " pst=%s",
6464 NS_LossyConvertUTF16toASCII(atomString).get());
6466 fputs("<\n", out);
6468 // Output the text
6469 aIndent++;
6471 IndentBy(out, aIndent);
6472 fputs("\"", out);
6473 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
6474 fputs("\"\n", out);
6476 aIndent--;
6477 IndentBy(out, aIndent);
6478 fputs(">\n", out);
6480 return NS_OK;
6482 #endif
6484 void nsTextFrame::AdjustSelectionPointsForBidi(SelectionDetails *sdptr,
6485 PRInt32 textLength,
6486 PRBool isRTLChars,
6487 PRBool isOddLevel,
6488 PRBool isBidiSystem)
6490 /* This adjustment is required whenever the text has been reversed by
6491 * Mozilla before rendering.
6493 * In theory this means any text whose Bidi embedding level has been
6494 * set by the Unicode Bidi algorithm to an odd value, but this is
6495 * only true in practice on a non-Bidi platform.
6497 * On a Bidi platform the situation is more complicated because the
6498 * platform will automatically reverse right-to-left characters; so
6499 * Mozilla reverses text whose natural directionality is the opposite
6500 * of its embedding level: right-to-left characters whose Bidi
6501 * embedding level is even (e.g. Visual Hebrew) or left-to-right and
6502 * neutral characters whose Bidi embedding level is odd (e.g. English
6503 * text with <bdo dir="rtl">).
6505 * The following condition is accordingly an optimization of
6506 * if ( (!isBidiSystem && isOddLevel) ||
6507 * (isBidiSystem &&
6508 * ((isRTLChars && !isOddLevel) ||
6509 * (!isRTLChars && isOddLevel))))
6511 if (isOddLevel ^ (isRTLChars && isBidiSystem)) {
6513 PRInt32 swap = sdptr->mStart;
6514 sdptr->mStart = textLength - sdptr->mEnd;
6515 sdptr->mEnd = textLength - swap;
6517 // temp fix for 75026 crasher until we fix the bidi code
6518 // the above bidi code cause mStart < 0 in some case
6519 // the problem is we have whitespace compression code in
6520 // nsTextTransformer which cause mEnd > textLength
6521 NS_ASSERTION((sdptr->mStart >= 0) , "mStart >= 0");
6522 if(sdptr->mStart < 0 )
6523 sdptr->mStart = 0;
6525 NS_ASSERTION((sdptr->mEnd >= 0) , "mEnd >= 0");
6526 if(sdptr->mEnd < 0 )
6527 sdptr->mEnd = 0;
6529 NS_ASSERTION((sdptr->mStart <= sdptr->mEnd), "mStart <= mEnd");
6530 if(sdptr->mStart > sdptr->mEnd)
6531 sdptr->mEnd = sdptr->mStart;
6534 return;
6537 void
6538 nsTextFrame::AdjustOffsetsForBidi(PRInt32 aStart, PRInt32 aEnd)
6540 AddStateBits(NS_FRAME_IS_BIDI);
6541 SetOffsets(aStart, aEnd);
6544 void
6545 nsTextFrame::SetOffsets(PRInt32 aStart, PRInt32 aEnd)
6547 mContentOffset = aStart;
6548 mContentLength = aEnd - aStart;
6552 * @return PR_TRUE if this text frame ends with a newline character. It should return
6553 * PR_FALSE if it is not a text frame.
6555 PRBool
6556 nsTextFrame::HasTerminalNewline() const
6558 const nsTextFragment* frag = mContent->GetText();
6559 if (frag && mContentLength > 0) {
6560 PRUnichar ch = frag->CharAt(mContentOffset + mContentLength - 1);
6561 if (ch == '\n')
6562 return PR_TRUE;
6564 return PR_FALSE;