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
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.
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 */
52 #include "nsHTMLParts.h"
54 #include "nsSplittableFrame.h"
55 #include "nsLineLayout.h"
57 #include "nsUnicharUtils.h"
58 #include "nsPresContext.h"
59 #include "nsIContent.h"
60 #include "nsStyleConsts.h"
61 #include "nsStyleContext.h"
63 #include "nsIFontMetrics.h"
64 #include "nsIRenderingContext.h"
65 #include "nsIPresShell.h"
68 #include "nsVoidArray.h"
70 #include "nsIDOMText.h"
71 #include "nsIDocument.h"
72 #include "nsIDeviceContext.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"
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"
98 #include "nsIAccessible.h"
99 #include "nsIAccessibilityService.h"
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"
111 nsTextFrameTextRunCache::Init() {
116 nsTextFrameTextRunCache::Shutdown() {
121 static NS_DEFINE_CID(kLECID
, NS_ULE_CID
);
126 #undef DEBUG_WORD_WRAPPING
131 #undef DEBUG_WORD_WRAPPING
136 // #define DEBUGWORDJUMP
138 #define kSZLIG 0x00DF
139 //----------------------------------------------------------------------
141 #define TEXT_BUF_SIZE 100
143 //----------------------------------------
145 struct nsAutoIndexBuffer
;
146 struct nsAutoPRUint8Buffer
;
150 const nsStyleFont
* mFont
;
151 const nsStyleText
* mText
;
152 nsIFontMetrics
* mNormalFont
;
153 nsIFontMetrics
* mSmallFont
;
154 nsIFontMetrics
* mLastFont
;
156 nscoord mWordSpacing
;
157 nscoord mLetterSpacing
;
159 nscoord mAveCharWidth
;
161 PRBool mPreformatted
;
162 PRInt32 mNumJustifiableCharacterToRender
;
163 PRInt32 mNumJustifiableCharacterToMeasure
;
164 nscoord mExtraSpacePerJustifiableCharacter
;
165 PRInt32 mNumJustifiableCharacterReceivingExtraJot
;
167 nsTextStyle(nsPresContext
* aPresContext
,
168 nsIRenderingContext
& aRenderingContext
,
174 // Contains extra style data needed only for painting (not reflowing)
175 class nsTextPaintStyle
: public nsTextStyle
{
179 nsISelectionController::SELECTION_NORMAL
,
181 nsISelectionController::SELECTION_IME_RAWINPUT
|
182 nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT
|
183 nsISelectionController::SELECTION_IME_CONVERTEDTEXT
|
184 nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
,
186 eNormalSelection
| eIMESelections
189 const nsStyleColor
* mColor
;
191 nsTextPaintStyle(nsPresContext
* aPresContext
,
192 nsIRenderingContext
& aRenderingContext
,
193 nsStyleContext
* aStyleContext
,
194 nsIContent
* aContent
,
195 PRInt16 aSelectionStatus
);
198 nscolor
GetTextColor();
199 void GetSelectionColors(nscolor
* aForeColor
,
201 PRBool
* aBackIsTransparent
);
202 void GetIMESelectionColors(SelectionType aSelectionType
,
205 PRBool
* aBackIsTransparent
);
206 // if this returns PR_FALSE, we don't need to draw underline.
207 PRBool
GetIMEUnderline(SelectionType aSelectionType
,
209 float* aRelativeSize
);
211 nsPresContext
* mPresContext
;
212 nsStyleContext
* mStyleContext
;
213 nsIContent
* mContent
;
214 PRInt16 mSelectionStatus
; // see nsIDocument.h SetDisplaySelection()
217 PRBool mInitCommonColors
;
219 PRInt32 mSufficientContrast
;
220 nscolor mFrameBackgroundColor
;
223 PRBool mInitSelectionColors
;
225 nscolor mSelectionTextColor
;
226 nscolor mSelectionBGColor
;
227 PRBool mSelectionBGIsTransparent
;
229 // IME selection colors and underline info
234 nscolor mBGIsTransparent
;
235 nscolor mUnderlineColor
;
237 nsIMEColor mIMEColor
[4];
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
,
260 class nsTextFrame
: public nsFrame
{
262 nsTextFrame(nsStyleContext
* aContext
) : nsFrame(aContext
)
264 NS_ASSERTION(mContentOffset
== 0, "Bogus content offset");
265 NS_ASSERTION(mContentLength
== 0, "Bogus content length");
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
,
277 nsIFrame
* aPrevInFlow
);
279 virtual void Destroy();
281 NS_IMETHOD
GetCursor(const nsPoint
& aPoint
,
282 nsIFrame::Cursor
& aCursor
);
284 NS_IMETHOD
CharacterDataChanged(nsPresContext
* aPresContext
,
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
);
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
;
313 aNextInFlow
->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION
);
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
));
339 NS_IMETHOD
List(FILE* out
, PRInt32 aIndent
) const;
340 NS_IMETHOD
GetFrameName(nsAString
& aResult
) const;
341 NS_IMETHOD_(nsFrameState
) GetDebugStateBits() const ;
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
,
357 NS_IMETHOD
SetSelected(nsPresContext
* aPresContext
,
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
,
378 NS_IMETHOD
GetChildFrameContainingOffset(PRInt32 inContentOffset
,
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;
395 NS_IMETHOD
GetAccessible(nsIAccessible
** aAccessible
);
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
,
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
{
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
,
435 PRBool aSkipWhitespace
,
438 PRBool aFirstLetterOK
,
439 PRBool aCanBreakBefore
,
440 PRBool aTrailingSpaceTrimmed
)
442 mOffset(aStartingOffset
),
445 mWrapping(aWrapping
),
446 mSkipWhitespace(aSkipWhitespace
),
447 mMeasureText(aMeasureText
),
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
,
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
,
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
,
506 SelectionDetails
*aDetails
= nsnull
);
508 void MeasureSmallCapsText(nsIRenderingContext
* aRenderingContext
,
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
,
522 TextReflowData
& aTextData
);
524 void GetTextDimensions(nsIRenderingContext
& aRenderingContext
,
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
,
534 PRUnichar
* aBuffer
, PRInt32 aLength
, PRBool aIsEndOfFrame
,
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
,
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
);
566 void ToCString(nsString
& aBuf
, PRInt32
* aTotalContentLength
) const;
569 PRInt32
GetContentOffset() { return mContentOffset
; }
570 PRInt32
GetContentLength() { return mContentLength
; }
573 virtual ~nsTextFrame();
575 nsIFrame
* mNextContinuation
;
576 PRInt32 mContentOffset
;
577 PRInt32 mContentLength
;
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
,
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
,
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..
607 inline PRBool
CanDarken(nsPresContext
* aPresContext
)
611 if (aPresContext
->GetBackgroundColorDraw()) {
614 if (aPresContext
->GetBackgroundImageDraw()) {
625 struct nsAutoIndexBuffer
{
627 ~nsAutoIndexBuffer();
629 nsresult
GrowTo(PRInt32 aAtLeast
);
633 PRInt32 mAutoBuffer
[TEXT_BUF_SIZE
];
636 nsAutoIndexBuffer::nsAutoIndexBuffer()
637 : mBuffer(mAutoBuffer
),
638 mBufferLen(TEXT_BUF_SIZE
)
641 memset(mAutoBuffer
, 0xdd, sizeof(mAutoBuffer
));
645 nsAutoIndexBuffer::~nsAutoIndexBuffer()
647 if (mBuffer
&& (mBuffer
!= mAutoBuffer
)) {
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
];
663 return NS_ERROR_OUT_OF_MEMORY
;
666 memset(newBuffer
, 0xdd, sizeof(PRInt32
) * newSize
);
668 memcpy(newBuffer
, mBuffer
, sizeof(PRInt32
) * mBufferLen
);
669 if (mBuffer
!= mAutoBuffer
) {
673 mBufferLen
= newSize
;
678 struct nsAutoPRUint8Buffer
{
679 nsAutoPRUint8Buffer();
680 ~nsAutoPRUint8Buffer();
682 nsresult
GrowTo(PRInt32 aAtLeast
);
686 PRUint8 mAutoBuffer
[TEXT_BUF_SIZE
];
689 nsAutoPRUint8Buffer::nsAutoPRUint8Buffer()
690 : mBuffer(mAutoBuffer
),
691 mBufferLen(TEXT_BUF_SIZE
)
694 memset(mAutoBuffer
, 0xdd, sizeof(mAutoBuffer
));
698 nsAutoPRUint8Buffer::~nsAutoPRUint8Buffer()
700 if (mBuffer
&& (mBuffer
!= mAutoBuffer
)) {
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
];
716 return NS_ERROR_OUT_OF_MEMORY
;
719 memset(newBuffer
, 0xdd, sizeof(PRUint8
) * newSize
);
721 memcpy(newBuffer
, mBuffer
, sizeof(PRUint8
) * mBufferLen
);
722 if (mBuffer
!= mAutoBuffer
) {
726 mBufferLen
= newSize
;
732 //----------------------------------------------------------------------
734 // Helper class for managing blinking text
736 class nsBlinkTimer
: public nsITimerCallback
740 virtual ~nsBlinkTimer();
744 void AddFrame(nsIFrame
* aFrame
);
746 PRBool
RemoveFrame(nsIFrame
* aFrame
);
748 PRInt32
FrameCount();
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; }
763 nsCOMPtr
<nsITimer
> mTimer
;
768 static nsBlinkTimer
* sTextBlinker
;
769 static PRUint32 sState
; // 0-2 == on; 3 == off
773 nsBlinkTimer
* nsBlinkTimer::sTextBlinker
= nsnull
;
774 PRUint32
nsBlinkTimer::sState
= 0;
777 static PRTime gLastTick
;
780 nsBlinkTimer::nsBlinkTimer()
784 nsBlinkTimer::~nsBlinkTimer()
787 sTextBlinker
= nsnull
;
790 void nsBlinkTimer::Start()
793 mTimer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
795 mTimer
->InitWithCallback(this, 250, nsITimer::TYPE_REPEATING_PRECISE
);
799 void nsBlinkTimer::Stop()
801 if (nsnull
!= mTimer
) {
806 NS_IMPL_ISUPPORTS1(nsBlinkTimer
, nsITimerCallback
)
808 void nsBlinkTimer::AddFrame(nsIFrame
* aFrame
) {
809 mFrames
.AppendElement(aFrame
);
810 if (1 == mFrames
.Count()) {
815 PRBool
nsBlinkTimer::RemoveFrame(nsIFrame
* aFrame
) {
816 PRBool rv
= mFrames
.RemoveElement(aFrame
);
817 if (0 == mFrames
.Count()) {
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
832 sState
= (sState
+ 1) % 4;
833 if (sState
== 1 || sState
== 2)
834 // States 0, 1, and 2 are all the same.
838 PRTime now
= PR_Now();
841 LL_SUB(delta
, now
, gLastTick
);
843 PR_snprintf(buf
, sizeof(buf
), "%lldusec", delta
);
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
);
861 nsresult
nsBlinkTimer::AddBlinkFrame(nsPresContext
* aPresContext
, nsIFrame
* aFrame
)
865 sTextBlinker
= new nsBlinkTimer
;
866 if (!sTextBlinker
) return NS_ERROR_OUT_OF_MEMORY
;
869 NS_ADDREF(sTextBlinker
);
871 sTextBlinker
->AddFrame(aFrame
);
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
);
890 //----------------------------------------------------------------------
892 nsTextStyle::nsTextStyle(nsPresContext
* aPresContext
,
893 nsIRenderingContext
& aRenderingContext
,
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
;
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
;
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
;
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();
938 unit
= mText
->mLetterSpacing
.GetUnit();
939 if (eStyleUnit_Coord
== unit
) {
940 mLetterSpacing
= mText
->mLetterSpacing
.GetCoordValue();
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
) &&
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
) {
967 res
= NS_RGB(NS_GET_R(colorA
) ^ 0xff,
968 NS_GET_G(colorA
) ^ 0xff,
969 NS_GET_B(colorA
) ^ 0xff);
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
),
986 mInitCommonColors(PR_FALSE
),
987 mInitSelectionColors(PR_FALSE
)
989 mPresContext
= aPresContext
;
990 mStyleContext
= aStyleContext
;
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()
1005 nsTextPaintStyle::EnsureSufficientContrast(nscolor
*aForeColor
, nscolor
*aBackColor
)
1008 if (!aForeColor
|| !aBackColor
)
1011 // If common colors are not initialized, mFrameBackgroundColor and
1012 // mSufficientContrast are not initialized.
1013 if (!mInitCommonColors
&& !InitCommonColors())
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
)
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
;
1037 nsTextPaintStyle::GetTextColor()
1039 return mColor
->mColor
;
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");
1056 *aForeColor
= mSelectionTextColor
;
1057 *aBackColor
= mSelectionBGColor
;
1058 *aBackIsTransparent
= mSelectionBGIsTransparent
;
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
);
1073 NS_ERROR("aSelectionType is invalid");
1076 if (!IMEColor
->mInit
)
1078 *aForeColor
= IMEColor
->mTextColor
;
1079 *aBackColor
= IMEColor
->mBGColor
;
1080 *aBackIsTransparent
= IMEColor
->mBGIsTransparent
;
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
);
1093 NS_ERROR("aSelectionType is invalid");
1096 if (!IMEColor
->mInit
)
1098 if (IMEColor
->mUnderlineColor
== NS_TRANSPARENT
||
1099 mIMEUnderlineRelativeSize
<= 0.0f
)
1102 *aLineColor
= IMEColor
->mUnderlineColor
;
1103 *aRelativeSize
= mIMEUnderlineRelativeSize
;
1108 nsTextPaintStyle::InitCommonColors()
1110 if (!mPresContext
|| !mStyleContext
)
1113 if (mInitCommonColors
)
1116 const nsStyleBackground
* bg
=
1117 nsCSSRendering::FindNonTransparentBackground(mStyleContext
);
1118 NS_ASSERTION(bg
, "Cannot find NonTransparentBackground.");
1119 mFrameBackgroundColor
= bg
->mBackgroundColor
;
1121 nsILookAndFeel
* look
= mPresContext
->LookAndFeel();
1125 nscolor defaultWindowBackgroundColor
, selectionTextColor
, selectionBGColor
;
1126 look
->GetColor(nsILookAndFeel::eColor_TextSelectBackground
,
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
,
1137 NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor
,
1140 mInitCommonColors
= PR_TRUE
;
1145 nsTextPaintStyle::InitSelectionColors()
1147 if (!mPresContext
|| !mStyleContext
)
1149 if (mInitSelectionColors
)
1152 mSelectionBGIsTransparent
= PR_FALSE
;
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.
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
;
1172 nsILookAndFeel
* look
= mPresContext
->LookAndFeel();
1176 nscolor selectionBGColor
;
1177 look
->GetColor(nsILookAndFeel::eColor_TextSelectBackground
,
1180 if (mSelectionStatus
== nsISelectionController::SELECTION_ATTENTION
) {
1181 look
->GetColor(nsILookAndFeel::eColor_TextSelectBackgroundAttention
,
1183 mSelectionBGColor
= EnsureDifferentColors(mSelectionBGColor
,
1185 } else if (mSelectionStatus
!= nsISelectionController::SELECTION_ON
) {
1186 look
->GetColor(nsILookAndFeel::eColor_TextSelectBackgroundDisabled
,
1188 mSelectionBGColor
= EnsureDifferentColors(mSelectionBGColor
,
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
,
1204 EnsureSufficientContrast(&mSelectionTextColor
, &mSelectionBGColor
);
1206 mInitSelectionColors
= PR_TRUE
;
1210 nsTextPaintStyle::nsIMEColor
*
1211 nsTextPaintStyle::GetIMEColor(SelectionType aSelectionType
)
1214 switch (aSelectionType
) {
1215 case nsISelectionController::SELECTION_IME_RAWINPUT
:
1216 index
= eIndexRawInput
;
1218 case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT
:
1219 index
= eIndexSelRawText
;
1221 case nsISelectionController::SELECTION_IME_CONVERTEDTEXT
:
1222 index
= eIndexConvText
;
1224 case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
:
1225 index
= eIndexSelConvText
;
1228 NS_ERROR("aSelectionType is Invalid");
1231 nsIMEColor
* IMEColor
= &mIMEColor
[index
];
1232 if (!IMEColor
->mInit
&& !InitIMEColors(aSelectionType
, IMEColor
))
1233 NS_ERROR("Fail to initialize IME color");
1238 nsTextPaintStyle::InitIMEColors(SelectionType aSelectionType
,
1239 nsIMEColor
* aIMEColor
)
1241 if (!mPresContext
|| !aIMEColor
)
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
;
1253 case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT
:
1254 foreColorID
= nsILookAndFeel::eColor_IMESelectedRawTextForeground
;
1255 backColorID
= nsILookAndFeel::eColor_IMESelectedRawTextBackground
;
1256 lineColorID
= nsILookAndFeel::eColor_IMESelectedRawTextUnderline
;
1258 case nsISelectionController::SELECTION_IME_CONVERTEDTEXT
:
1259 foreColorID
= nsILookAndFeel::eColor_IMEConvertedTextForeground
;
1260 backColorID
= nsILookAndFeel::eColor_IMEConvertedTextBackground
;
1261 lineColorID
= nsILookAndFeel::eColor_IMEConvertedTextUnderline
;
1263 case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
:
1264 foreColorID
= nsILookAndFeel::eColor_IMESelectedConvertedTextForeground
;
1265 backColorID
= nsILookAndFeel::eColor_IMESelectedConvertedTextBackground
;
1266 lineColorID
= nsILookAndFeel::eColor_IMESelectedConvertedTextUnderline
;
1269 NS_ERROR("aSelectionType is Invalid");
1273 nsILookAndFeel
* look
= mPresContext
->LookAndFeel();
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");
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
);
1327 nsTextPaintStyle::GetResolvedForeColor(nscolor aColor
,
1328 nscolor aDefaultForeColor
,
1331 if (aColor
== NS_SAME_AS_FOREGROUND_COLOR
)
1332 return aDefaultForeColor
;
1334 if (aColor
!= NS_40PERCENT_FOREGROUND_COLOR
)
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");
1357 return accService
->CreateHTMLTextAccessible(NS_STATIC_CAST(nsIFrame
*, this), aAccessible
);
1360 return NS_ERROR_FAILURE
;
1365 //-----------------------------------------------------------------------------
1367 nsTextFrame::Init(nsIContent
* aContent
,
1369 nsIFrame
* aPrevInFlow
)
1371 NS_PRECONDITION(aContent
->IsNodeOfType(nsINode::eTEXT
),
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();
1388 nsTextFrame::Destroy()
1390 if (mNextContinuation
) {
1391 mNextContinuation
->SetPrevInFlow(nsnull
);
1393 // Let the base class destroy the frame
1397 class nsContinuingTextFrame
: public nsTextFrame
{
1399 friend nsIFrame
* NS_NewContinuingTextFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
);
1401 NS_IMETHOD
Init(nsIContent
* aContent
,
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
);
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
);
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
);
1441 nsContinuingTextFrame(nsStyleContext
* aContext
) : nsTextFrame(aContext
) {}
1442 nsIFrame
* mPrevContinuation
;
1446 nsContinuingTextFrame::Init(nsIContent
* aContent
,
1448 nsIFrame
* aPrevInFlow
)
1450 nsresult rv
= nsTextFrame::Init(aContent
, aParent
, aPrevInFlow
);
1453 nsIFrame
* nextContinuation
= aPrevInFlow
->GetNextContinuation();
1454 // Hook the frame into the flow
1455 SetPrevInFlow(aPrevInFlow
);
1456 aPrevInFlow
->SetNextInFlow(this);
1458 if (aPrevInFlow
->GetStateBits() & NS_FRAME_IS_BIDI
) {
1460 aPrevInFlow
->GetOffsets(start
, mContentOffset
);
1462 nsPropertyTable
*propTable
= PresContext()->PropertyTable();
1463 propTable
->SetProperty(this, nsGkAtoms::embeddingLevel
,
1464 propTable
->GetProperty(aPrevInFlow
, nsGkAtoms::embeddingLevel
),
1466 propTable
->SetProperty(this, nsGkAtoms::baseLevel
,
1467 propTable
->GetProperty(aPrevInFlow
, nsGkAtoms::baseLevel
),
1469 propTable
->SetProperty(this, nsGkAtoms::charType
,
1470 propTable
->GetProperty(aPrevInFlow
, nsGkAtoms::charType
),
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
1487 nsContinuingTextFrame::Destroy()
1489 if (mPrevContinuation
|| mNextContinuation
) {
1490 nsSplittableFrame::RemoveFromFlow(this);
1492 // Let the base class destroy the frame
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));
1504 firstInFlow
= previous
;
1505 previous
= firstInFlow
->GetPrevInFlow();
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
));
1518 firstContinuation
= previous
;
1519 previous
= firstContinuation
->GetPrevContinuation();
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
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
);
1549 nsContinuingTextFrame::AddInlineMinWidth(nsIRenderingContext
*aRenderingContext
,
1550 InlineMinWidthData
*aData
)
1552 // Do nothing, since the first-in-flow accounts for everything.
1557 nsContinuingTextFrame::AddInlinePrefWidth(nsIRenderingContext
*aRenderingContext
,
1558 InlinePrefWidthData
*aData
)
1560 // Do nothing, since the first-in-flow accounts for everything.
1564 //DRAW SELECTION ITERATOR USED FOR TEXTFRAMES ONLY
1565 //helper class for drawing multiply selected text
1566 class DrawSelectionIterator
1569 DrawSelectionIterator(const SelectionDetails
*aSelDetails
, PRUnichar
*aText
,
1570 PRUint32 aTextLength
, nsTextPaintStyle
*aTextStyle
,
1571 SelectionType aCareSelections
);
1572 ~DrawSelectionIterator();
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
);
1603 PRUint32 mCurrentIdx
;
1604 PRUint32 mCurrentLength
;
1605 nsTextPaintStyle
* mOldStyle
;//base new styles on this one???
1606 const SelectionDetails
*mDetails
;
1611 void FillCurrentData();
1614 DrawSelectionIterator::DrawSelectionIterator(const SelectionDetails
*aSelDetails
,
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
;
1626 mLength
= aTextLength
;
1634 mDone
= (PRBool
)(mCurrentIdx
>=mLength
);
1638 //special case for 1 selection. later
1639 const SelectionDetails
*details
= aSelDetails
;
1640 if (details
->mNext
) {
1642 } else if (details
->mStart
== details
->mEnd
) {
1643 // no collapsed selections here!
1646 } else if (!(details
->mType
& aCareSelections
)) {
1647 //if all we have is selection we DONT care about, do nothing
1652 mTypes
= new PRUint8
[mLength
];
1655 memset(mTypes
, 0, mLength
);
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?");
1665 mTypes
[i
] |= details
->mType
;
1668 details
= details
->mNext
;
1671 // we have details but none that we care about.
1674 mDone
= PR_TRUE
; // we are finished
1679 DrawSelectionIterator::~DrawSelectionIterator()
1686 DrawSelectionIterator::FillCurrentData()
1690 mCurrentIdx
+= mCurrentLength
; // advance to this chunk
1692 if (mCurrentIdx
>= mLength
)
1699 if (mCurrentIdx
< (PRUint32
)mDetails
->mStart
)
1701 mCurrentLength
= mDetails
->mStart
;
1703 else if (mCurrentIdx
== (PRUint32
)mDetails
->mStart
)
1705 mCurrentLength
= mDetails
->mEnd
-mCurrentIdx
;
1707 else if (mCurrentIdx
> (PRUint32
)mDetails
->mStart
)//last unselected part
1709 mCurrentLength
= mLength
- mDetails
->mEnd
;
1714 uint8 typevalue
= mTypes
[mCurrentIdx
];
1715 while (mCurrentIdx
+mCurrentLength
< mLength
&& typevalue
== mTypes
[mCurrentIdx
+mCurrentLength
])
1720 // never overrun past mLength
1721 if (mCurrentIdx
+mCurrentLength
> mLength
)
1723 mCurrentLength
= mLength
- mCurrentIdx
;
1728 DrawSelectionIterator::First()
1734 if (!mTypes
&& mDetails
->mStart
== mDetails
->mEnd
)//no collapsed selections here!
1736 mDone
= (mCurrentIdx
+mCurrentLength
) >= mLength
;
1744 DrawSelectionIterator::Next()
1746 if (mDone
|| !mInit
)
1748 FillCurrentData();//advances to next chunk
1753 DrawSelectionIterator::IsLast()
1755 return mDone
|| !mInit
|| mCurrentIdx
+ mCurrentLength
>= mLength
;
1759 DrawSelectionIterator::IsDone()
1761 return mDone
|| !mInit
;
1766 DrawSelectionIterator::CurrentTextUnicharPtr()
1768 return mUniStr
+mCurrentIdx
;
1772 DrawSelectionIterator::CurrentTextCStrPtr()
1774 return mCStr
+mCurrentIdx
;
1778 DrawSelectionIterator::CurrentLength()
1780 return mCurrentLength
;
1784 DrawSelectionIterator::GetSelectionColors(nscolor
*aForeColor
,
1785 nscolor
*aBackColor
,
1786 PRBool
*aBackIsTransparent
)
1790 if (mTypes
[mCurrentIdx
] & nsTextPaintStyle::eNormalSelection
) {
1791 mOldStyle
->GetSelectionColors(aForeColor
, aBackColor
,
1792 aBackIsTransparent
);
1797 if (mTypes
[mCurrentIdx
] & nsTextPaintStyle::eIMESelections
) {
1798 mOldStyle
->GetIMESelectionColors(mTypes
[mCurrentIdx
],
1799 aForeColor
, aBackColor
,
1800 aBackIsTransparent
);
1805 // Non-supported Selection or Non-selection text
1806 *aBackIsTransparent
= PR_FALSE
;
1807 *aForeColor
= mOldStyle
->GetTextColor();
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)
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)
1866 #define DEBUG_VERIFY_NOT_DIRTY(state)
1870 NS_NewTextFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
1872 return new (aPresShell
) nsTextFrame(aContext
);
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);
1890 nsTextFrame::GetDocument(nsPresContext
* aPresContext
)
1892 nsIDocument
*result
= nsnull
;
1894 result
= mContent
->GetDocument();
1896 if (!result
&& aPresContext
) {
1897 result
= aPresContext
->PresShell()->GetDocument();
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
;
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.");
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.");
1954 nsTextFrame::CharacterDataChanged(nsPresContext
* aPresContext
,
1958 nsIFrame
* targetTextFrame
= this;
1961 nsTextFrame
* frame
= NS_STATIC_CAST(nsTextFrame
*, GetLastContinuation());
1962 frame
->mState
&= ~TEXT_WHITESPACE_FLAGS
;
1963 targetTextFrame
= frame
;
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
1971 nsTextFrame
* textFrame
= this;
1973 textFrame
->mState
&= ~TEXT_WHITESPACE_FLAGS
;
1974 textFrame
->mContentOffset
= 0;
1975 textFrame
->mContentLength
= 0;
1976 textFrame
= NS_STATIC_CAST(nsTextFrame
*, textFrame
->GetNextContinuation());
1980 textFrame
->mState
|= NS_FRAME_IS_DIRTY
;
1984 // Ask the parent frame to reflow me.
1985 aPresContext
->GetPresShell()->FrameNeedsReflow(targetTextFrame
,
1986 nsIPresShell::eStyleChange
,
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
{
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
);
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")
2013 nsDisplayText::Paint(nsDisplayListBuilder
* aBuilder
,
2014 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
2015 NS_STATIC_CAST(nsTextFrame
*, mFrame
)->
2016 PaintText(*aCtx
, aBuilder
->ToReferenceFrame(mFrame
));
2020 nsTextFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
2021 const nsRect
& aDirtyRect
,
2022 const nsDisplayListSet
& aLists
)
2024 if (!IsVisibleForPainting(aBuilder
))
2027 DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame");
2029 if ((0 != (mState
& TEXT_BLINK_ON
)) && nsBlinkTimer::GetBlinkIsOff())
2032 return aLists
.Content()->AppendNewToTop(new (aBuilder
) nsDisplayText(this));
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
),
2045 PRInt16 selectionValue
;
2046 if (NS_FAILED(GetSelectionStatus(presContext
, selectionValue
)))
2047 selectionValue
= nsISelectionController::SELECTION_NORMAL
;
2049 nsTextPaintStyle
ts(presContext
, aRenderingContext
, mStyleContext
, content
,
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
);
2057 // Get the text fragment
2058 const nsTextFragment
* frag
= mContent
->GetText();
2063 // Choose rendering pathway based on rendering context performance
2064 // hint, whether it needs to be transformed, and whether it's
2066 PRBool hasMultiByteChars
= (0 != (mState
& TEXT_HAS_MULTIBYTE
));
2068 aRenderingContext
.GetHints(hints
);
2071 PRBool bidiEnabled
= presContext
->BidiEnabled();
2073 const PRBool bidiEnabled
= PR_FALSE
;
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
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
);
2087 PaintAsciiText(presContext
, aRenderingContext
, sc
, ts
, aPt
.x
, aPt
.y
);
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
)
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
2113 nsTextFrame::IsJustifiableCharacter(PRUnichar aChar
, PRBool aLangIsCJ
)
2115 if (0x20u
== aChar
|| 0xa0u
== aChar
)
2117 if (aChar
< 0x2150u
)
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)
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();
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
;
2164 rv
= acx
->GetClusterInfo(aText
, aLength
, aClusterBuffer
.mBuffer
);
2167 memset(aClusterBuffer
.mBuffer
, 1, sizeof(PRInt8
) * aLength
);
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.
2191 nsTextFrame::PrepareUnicodeText(nsTextTransformer
& aTX
,
2192 nsAutoIndexBuffer
* aIndexBuffer
,
2193 nsAutoTextBuffer
* aTextBuffer
,
2195 PRBool aForceArabicShaping
,
2196 PRIntn
* aJustifiableCharCount
,
2197 PRBool aRemoveMultipleTrimmedWS
)
2199 // Setup transform to operate starting in the content at our content
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");
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
;
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
;
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
);
2269 if ('\t' == bp
[0]) {
2270 PRInt32 spaces
= 8 - (7 & column
);
2273 while (--spaces
>= 0) {
2276 // XXX This is a one to many mapping that I think isn't handled well
2277 if (nsnull
!= indexp
) {
2282 else if ('\n' == bp
[0]) {
2283 if (nsnull
!= indexp
) {
2288 else if (nsnull
!= indexp
) {
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
;
2299 // Point mapping indicies at each content index in the word
2300 PRInt32 i
= contentLen
;
2302 *indexp
++ = strInx
++;
2308 PRInt32 i
= contentLen
;
2309 if (nsnull
!= indexp
) {
2310 // Point mapping indices at each content index in the word
2311 if (!wasTransformed
) {
2313 *indexp
++ = strInx
++;
2317 PRBool caseChanged
=
2318 textTransform
== NS_STYLE_TEXT_TRANSFORM_UPPERCASE
||
2319 textTransform
== NS_STYLE_TEXT_TRANSFORM_CAPITALIZE
;
2321 PRUnichar ch
= aTX
.GetContentCharAt(mContentOffset
+
2322 indexp
- aIndexBuffer
->mBuffer
);
2323 if (IS_DISCARDED(ch
) || ch
== '\n') {
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
);
2340 *indexp
++ = strInx
++;
2341 // Point any capitalized German ß to 'SS'
2342 if (caseChanged
&& ch
== kSZLIG
&& *tp
== PRUnichar('S')) {
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
)) {
2361 textLength
+= wordLen
;
2363 if (textBuffer
!= nsnull
) {
2364 memcpy(textBuffer
->mBuffer
+ dstOffset
, bp
,
2365 sizeof(PRUnichar
)*wordLen
);
2367 dstOffset
+= wordLen
;
2372 NS_ASSERTION(indexp
<= aIndexBuffer
->mBuffer
+ aIndexBuffer
->mBufferLen
,
2373 "yikes - we just overwrote memory");
2376 NS_ASSERTION(dstOffset
<= textBuffer
->mBufferLen
,
2377 "yikes - we just overwrote memory");
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
))
2392 if (!aRemoveMultipleTrimmedWS
)
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
;
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
))
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)
2437 RenderSelectionCursor(nsIRenderingContext
& aRenderingContext
,
2438 nscoord dx
, nscoord dy
, nscoord aHeight
,
2439 nscolor aCursorColor
)
2442 nscoord ox
= aHeight
/ 4;
2445 nscoord y0
= dy
+ aHeight
;
2446 pnts
[0].x
= x0
- ox
;
2449 pnts
[1].y
= y0
- oy
;
2450 pnts
[2].x
= x0
+ ox
;
2452 pnts
[3].x
= x0
- ox
;
2455 // Draw little blue triangle
2456 aRenderingContext
.SetColor(aCursorColor
);
2457 aRenderingContext
.FillPolygon(pnts
, 4);
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();
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
;
2526 context
= context
->GetParent();
2529 hasDecorations
= context
->HasTextDecorations();
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
);
2555 nsRect rect
= GetRect();
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
));
2563 if ((start
< end
) && ((aLength
- start
) > 0))
2565 //aDetails allready processed to have offsets from frame start not content offsets
2570 if (aDetails
->mStart
> 0){
2573 for (i
= 0; i
< start
;i
++){
2574 startOffset
+= *sp
++;
2578 aRenderingContext
.GetWidth(aText
, start
, startOffset
);
2581 for (i
= start
; i
< end
;i
++){
2582 textWidth
+= *sp
++;
2586 aRenderingContext
.GetWidth(aText
+ start
,
2587 PRUint32(end
- start
), textWidth
);
2592 nscoord offset
, size
;
2593 nscoord baseline
= mAscent
;
2594 switch (aDetails
->mType
) {
2595 case nsISelectionController::SELECTION_NORMAL
:
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
);
2613 aRenderingContext
.DrawLine(aX
+ startOffset
,
2614 aY
+ baseline
- offset
,
2615 aX
+ startOffset
+ textWidth
,
2616 aY
+ baseline
- offset
);
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
,
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
));
2642 NS_ASSERTION(0,"what type of selection do i not know about?");
2647 aDetails
= aDetails
->mNext
;
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??
2661 *aOffset
= mContentOffset
;
2662 *aLength
= mContentLength
;
2663 nsIFrame
*parent
= GetParent();
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();
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();
2678 nsIFrame
*firstParent
= grandParent
->GetFirstChild(nsnull
);
2682 if (firstParent
== parent
) //then our parent is the first child of granddad. use BEFORE
2688 *aOffset
= (*aContent
)->GetChildCount();
2696 //END GENERATED BLOCK
2699 *aContent
= mContent
;
2700 NS_IF_ADDREF(*aContent
);
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
);
2721 NS_IF_ADDREF(*aPresShell
= aPresContext
->GetPresShell());
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
;
2736 aIsPaginated
= PR_FALSE
;
2737 aDisplayingSelection
=
2738 (aSelectionValue
> nsISelectionController::SELECTION_HIDDEN
);
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
);
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();
2767 return NS_ERROR_FAILURE
;
2769 aIsSelected
= (GetStateBits() & NS_FRAME_SELECTED_CONTENT
) == NS_FRAME_SELECTED_CONTENT
;
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
);
2793 nsTextFrame::IsTextInSelection()
2795 nsCOMPtr
<nsISelectionController
> selCon
;
2796 nsCOMPtr
<nsIPresShell
> shell
;
2797 PRBool displaySelection
;
2800 PRBool hideStandardSelection
;
2801 PRInt16 selectionValue
;
2802 nsPresContext
* presContext
= PresContext();
2803 if (NS_FAILED(GetTextInfoForPainting(presContext
,
2804 getter_AddRefs(shell
),
2805 getter_AddRefs(selCon
),
2809 hideStandardSelection
,
2814 // Make enough space to transform
2815 nsAutoTextBuffer paintBuffer
;
2816 nsAutoIndexBuffer indexBuffer
;
2817 if (NS_FAILED(indexBuffer
.GrowTo(mContentLength
+ 1))) {
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
);
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
;
2844 nsresult rv
= GetContentAndOffsetsForSelection(presContext
,
2845 getter_AddRefs(content
),
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
;
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
;
2869 while ((sdptr
= details
->mNext
) != nsnull
) {
2880 nsTextFrame::IsVisibleInSelection(nsISelection
* aSelection
)
2882 // Check the quick way first
2883 PRBool isSelected
= (mState
& NS_FRAME_SELECTED_CONTENT
) == NS_FRAME_SELECTED_CONTENT
;
2887 return IsTextInSelection();
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
;
2902 PRBool hideStandardSelection
;
2903 PRInt16 selectionValue
;
2905 PRBool isOddLevel
= PR_FALSE
;
2908 if (NS_FAILED(GetTextInfoForPainting(aPresContext
,
2909 getter_AddRefs(shell
),
2910 getter_AddRefs(selCon
),
2914 hideStandardSelection
,
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))) {
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
);
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
)
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
2972 aRenderingContext
.SetRightToLeftText(PR_TRUE
);
2974 nsBidiPresUtils
* bidiUtils
= aPresContext
->GetBidiUtils();
2977 PRInt32 rememberTextLength
= textLength
;
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");
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
);
2999 { //we draw according to selection rules
3000 SelectionDetails
*details
= nsnull
;
3001 nsCOMPtr
<nsIContent
> content
;
3004 nsresult rv
= GetContentAndOffsetsForSelection(aPresContext
,
3005 getter_AddRefs(content
),
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
;
3015 sdptr
->mStart
= ip
[sdptr
->mStart
] - mContentOffset
;
3016 sdptr
->mEnd
= ip
[sdptr
->mEnd
] - mContentOffset
;
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");
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
,
3033 if (sdptr
->mStart
> sdptr
->mEnd
) /* Left Edge */
3034 sdptr
->mEnd
= start
;
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
;
3048 AdjustSelectionPointsForBidi(sdptr
, textLength
, CHARTYPE_IS_RTL(charType
), isOddLevel
, isBidiSystem
);
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
3064 // See if this rendering backend supports getting cluster
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
;
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(¤tFGColor
,
3093 &isCurrentBKColorTransparent
);
3095 if (currentlength
> 0)
3099 rv
= aRenderingContext
.GetRangeWidth(text
, textLength
, currenttext
- text
,
3100 (currenttext
- text
) + currentlength
,
3102 newWidth
= nscoord(tmpWidth
);
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
);
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();
3142 if (!isRightToLeftOnBidiPlatform
)
3144 currentX
+= newWidth
; // increment twips X start
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
);
3161 while ((sdptr
= details
->mNext
) != nsnull
) {
3169 if (isRightToLeftOnBidiPlatform
) {
3170 // indicate that future text should not be reordered with
3171 // right-to-left base direction
3172 aRenderingContext
.SetRightToLeftText(PR_FALSE
);
3178 //measure Spaced Textvoid
3180 nsTextFrame::GetPositionSlowly(nsIRenderingContext
* aRendContext
,
3181 const nsPoint
& aPoint
,
3182 nsIContent
** aNewContent
,
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.
3219 *aNewContent
= mContent
;
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.
3230 *aNewContent
= nsnull
;
3234 // Transform text from content into renderable form
3235 nsTextTransformer
tx(PresContext());
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;
3253 PRUnichar
*tStart
, *tEnd
;
3255 for (tStart
= paintBuffer
.mBuffer
, tEnd
= tStart
+ textLength
- 1; tEnd
> tStart
; tStart
++, tEnd
--) {
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
);
3270 aOffset
= mContentOffset
+ textLength
-
3271 GetLengthSlowly(*aRendContext
, ts
, paintBuffer
.mBuffer
,
3272 textLength
, PR_TRUE
, adjustedX
);
3275 aOffset
= mContentOffset
+
3276 GetLengthSlowly(*aRendContext
, ts
,paintBuffer
.mBuffer
,
3277 textLength
, PR_TRUE
, adjustedX
);
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
;
3289 *aNewContent
= mContent
;
3291 (*aNewContent
)->AddRef();
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
,
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];
3326 sp0
= new nscoord
[aLength
*2];
3329 else if (aLength
> TEXT_BUF_SIZE
) {
3330 bp0
= new PRUnichar
[aLength
];
3332 sp0
= new nscoord
[aLength
];
3335 PRUnichar
* bp
= bp0
;
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
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
;
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
;
3378 runStart
= bp
= bp0
;
3382 aRenderingContext
.SetFont(nextFont
);
3383 lastFont
= nextFont
;
3385 if (nextFont
== aTextStyle
.mSmallFont
) {
3387 // German szlig should be expanded to "SS".
3389 upper_ch
= (PRUnichar
)'S';
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.
3399 width
+= glyphWidth
;
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
3417 // put the width into the space buffer
3418 width
+= 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
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
) {
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
;
3462 if (sp0
!= spacingMem
) {
3468 nsTextFrame::MeasureSmallCapsText(nsIRenderingContext
* aRenderingContext
,
3469 nsTextStyle
& aTextStyle
,
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
;
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();
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
))) {
3513 // German szlig should be expanded to "SS".
3515 upper_ch
= (PRUnichar
)'S';
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
;
3525 glyphDimensions
.width
+= glyphDimensions
.width
;
3527 else if (ch
== ' ' || ch
== CH_NBSP
) {
3528 glyphDimensions
.width
= aStyle
.mSpaceWidth
+ aStyle
.mLetterSpacing
3529 + aStyle
.mWordSpacing
;
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
);
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
);
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
;
3564 aStyle
.mLastFont
= lastFont
;
3565 *aDimensionsResult
= sum
;
3570 // XXX factor in logic from RenderString into here; gaps, justification, etc.
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
);
3582 nsTextFrame::GetLengthSlowly(nsIRenderingContext
& aRenderingContext
,
3583 nsTextStyle
& aStyle
,
3584 PRUnichar
* aBuffer
, PRInt32 aLength
, PRBool aIsEndOfFrame
,
3587 nsTextDimensions dimensions
;
3588 dimensions
.width
= aWidth
;
3589 return GetTextDimensionsOrLength(aRenderingContext
,aStyle
,
3590 aBuffer
,aLength
,aIsEndOfFrame
,&dimensions
,PR_FALSE
);
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
;
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
;
3645 PRBool hideStandardSelection
;
3646 PRInt16 selectionValue
;
3647 if (NS_FAILED(GetTextInfoForPainting(aPresContext
,
3648 getter_AddRefs(shell
),
3649 getter_AddRefs(selCon
),
3653 hideStandardSelection
,
3660 canDarkenColor
= CanDarken(aPresContext
);
3663 // Make enough space to transform
3664 nsAutoTextBuffer paintBuffer
;
3665 nsAutoIndexBuffer indexBuffer
;
3666 if (NS_FAILED(indexBuffer
.GrowTo(mContentLength
+ 1))) {
3669 nscoord width
= mRect
.width
;
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
) {
3683 PRBool isRightToLeftOnBidiPlatform
= PR_FALSE
;
3684 PRBool isBidiSystem
= PR_FALSE
;
3685 PRBool isOddLevel
= PR_FALSE
;
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();
3699 isOddLevel
= NS_GET_EMBEDDING_LEVEL(this) & 1;
3700 charType
= (nsCharType
)NS_PTR_TO_INT32(aPresContext
->PropertyTable()->GetProperty(this, nsGkAtoms::charType
));
3702 PRInt32 rememberTextLength
= textLength
;
3704 isRightToLeftOnBidiPlatform
= (!paintCharByChar
&&
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
3712 aRenderingContext
.SetRightToLeftText(PR_TRUE
);
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");
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
);
3734 SelectionDetails
*details
= nsnull
;
3735 nsCOMPtr
<nsIContent
> content
;
3738 nsresult rv
= GetContentAndOffsetsForSelection(aPresContext
,
3739 getter_AddRefs(content
),
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
;
3749 sdptr
->mStart
= ip
[sdptr
->mStart
] - mContentOffset
;
3750 sdptr
->mEnd
= ip
[sdptr
->mEnd
] - mContentOffset
;
3752 AdjustSelectionPointsForBidi(sdptr
, textLength
,
3753 CHARTYPE_IS_RTL(charType
), isOddLevel
,
3754 (paintCharByChar
) ? PR_FALSE
: isBidiSystem
);
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
;
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(¤tFGColor
,
3777 &isCurrentBKColorTransparent
);
3778 PRBool isEndOfFrame
= iter
.IsLast();
3779 GetTextDimensions(aRenderingContext
, aTextStyle
, currenttext
,
3780 (PRInt32
)currentlength
, isEndOfFrame
, &newDimensions
);
3781 if (newDimensions
.width
)
3784 if (isRightToLeftOnBidiPlatform
)
3785 currentX
-= newDimensions
.width
;
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
);
3811 if (!isRightToLeftOnBidiPlatform
)
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;
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
);
3829 while ((sdptr
= details
->mNext
) != nsnull
) {
3837 if (isRightToLeftOnBidiPlatform
) {
3838 // indicate that future text should not be reordered with
3839 // right-to-left base direction
3840 aRenderingContext
.SetRightToLeftText(PR_FALSE
);
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
;
3860 PRBool hideStandardSelection
;
3861 PRInt16 selectionValue
;
3862 if (NS_FAILED(GetTextInfoForPainting(aPresContext
,
3863 getter_AddRefs(shell
),
3864 getter_AddRefs(selCon
),
3868 hideStandardSelection
,
3874 canDarkenColor
= CanDarken(aPresContext
);
3877 // Get the text fragment
3878 const nsTextFragment
* frag
= mContent
->GetText();
3883 // Make enough space to transform
3884 nsAutoTextBuffer unicodePaintBuffer
;
3885 nsAutoIndexBuffer indexBuffer
;
3886 if (displaySelection
) {
3887 if (NS_FAILED(indexBuffer
.GrowTo(mContentLength
+ 1))) {
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;
3900 char paintBufMem
[TEXT_BUF_SIZE
];
3901 char* paintBuf
= paintBufMem
;
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
];
3921 char* dst
= paintBuf
;
3922 char* end
= dst
+ textLength
;
3923 PRUnichar
* src
= unicodePaintBuffer
.mBuffer
;
3925 *dst
++ = (char) ((unsigned char) *src
++);
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
)) {
3943 // See if the text ends in a newline
3944 if ((textLength
> 0) && (text
[textLength
- 1] == '\n')) {
3947 NS_ASSERTION(textLength
>= 0, "bad text length");
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
);
3970 SelectionDetails
*details
= nsnull
;
3971 nsCOMPtr
<nsIContent
> content
;
3974 nsresult rv
= GetContentAndOffsetsForSelection(aPresContext
,
3975 getter_AddRefs(content
),
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
;
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
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
;
4015 rv
= aRenderingContext
.GetRangeWidth(text
, textLength
, currenttext
- text
,
4016 (currenttext
- text
) + currentlength
,
4018 newWidth
= nscoord(tmpWidth
);
4021 rv
= aRenderingContext
.GetWidth(currenttext
, currentlength
,newWidth
); //ADJUST FOR CHAR SPACING
4024 PRBool isSelection
= iter
.GetSelectionColors(¤tFGColor
,
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
);
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
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
);
4074 while ((sdptr
= details
->mNext
) != nsnull
) {
4084 if (paintBuf
!= paintBufMem
) {
4089 // XXX I don't really want to rewrite GetPositionHelper, so I'm doing this
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
);
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 //---------------------------------------------------------------------------
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");
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();
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
,
4135 aContentOffsetEnd
= aContentOffset
;
4139 // Make enough space to transform
4140 nsAutoTextBuffer paintBuffer
;
4141 nsAutoIndexBuffer indexBuffer
;
4142 rv
= indexBuffer
.GrowTo(mContentLength
+ 1);
4143 if (NS_FAILED(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());
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
;
4162 PRInt32
* ip
= indexBuffer
.mBuffer
;
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
;
4173 indx
= rendContext
->GetPosition(text
, textLength
, aPoint
);
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
4187 PRBool found
= nsLayoutUtils::BinarySearchForPosition(rendContext
, text
, 0, 0, 0,
4188 PRInt32(textLength
),
4189 PRInt32(aPoint
.x
) , //go to local coordinates
4194 if (NS_IS_HIGH_SURROGATE(text
[indx
]))
4195 rendContext
->GetWidth(&text
[indx
], 2, charWidth
);
4197 rendContext
->GetWidth(text
[indx
], charWidth
);
4201 if (getReversedPos
) {
4202 if (mRect
.width
- aPoint
.x
> textWidth
+charWidth
) {
4208 if ((aPoint
.x
) > textWidth
+charWidth
) {
4214 aContentOffset
= indx
+ mContentOffset
;
4215 //reusing wordBufMem
4217 for (i
= 0; i
< mContentLength
; i
++){
4218 if ((ip
[i
] >= aContentOffset
) && //reverse mapping
4219 (! NS_IS_LOW_SURROGATE(paintBuffer
.mBuffer
[ip
[i
]-mContentOffset
]))) {
4223 aContentOffset
= i
+ mContentOffset
;
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
)
4232 curindx
= ip
[aContentOffset
- mContentOffset
] - mContentOffset
;
4236 aContentOffsetEnd
= aContentOffset
;
4237 NS_ASSERTION(i
<= mContentLength
, "offset we got from binary search is messed up");
4239 *aNewContent
= mContent
;
4241 (*aNewContent
)->AddRef();
4248 // [HACK] Foward Declarations
4249 void ForceDrawFrame(nsFrame
* aFrame
);
4251 //null range means the whole thing
4253 nsTextFrame::SetSelected(nsPresContext
* aPresContext
,
4254 nsIDOMRange
*aRange
,
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
;
4264 if (aSelected
&& ParentDisablesSelection())
4268 PRBool isSelected
= ((GetStateBits() & NS_FRAME_SELECTED_CONTENT
) == NS_FRAME_SELECTED_CONTENT
);
4269 if (!aSelected
&& !isSelected
) //already set thanks
4275 // check whether style allows selection
4277 IsSelectable(&selectable
, nsnull
);
4279 return NS_OK
;//do not continue no selection for this frame.
4281 PRBool found
= PR_FALSE
;
4283 //lets see if the range contains us, if so we must redraw!
4284 nsCOMPtr
<nsIDOMNode
> endNode
;
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
)
4299 if (thisNode
== endNode
)
4301 if (endOffset
== startOffset
) //no need to redraw since drawing takes place with cursor
4304 if (mContentOffset
> endOffset
)
4309 else if (thisNode
== endNode
)
4311 if (mContentOffset
< endOffset
)
4324 // null range means the whole thing
4329 AddStateBits(NS_FRAME_SELECTED_CONTENT
);
4331 {//we need to see if any other selection available.
4332 SelectionDetails
*details
= nsnull
;
4333 nsCOMPtr
<nsIContent
> content
;
4337 nsresult rv
= GetContentAndOffsetsForSelection(aPresContext
,
4338 getter_AddRefs(content
),
4340 if (NS_SUCCEEDED(rv
) && content
) {
4341 details
= GetFrameSelection()->LookUpSelection(content
, offset
,
4343 // PR_TRUE last param used here! we need to see if we are still selected. so no shortcut
4346 RemoveStateBits(NS_FRAME_SELECTED_CONTENT
);
4349 SelectionDetails
*sdptr
= details
;
4350 while ((sdptr
= details
->mNext
) != nsnull
) {
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
4361 Invalidate(nsRect(0, 0, mRect
.width
, mRect
.height
), PR_FALSE
);
4363 if (aSpread
== eSpreadDown
)
4365 nsIFrame
* frame
= GetPrevContinuation();
4367 frame
->SetSelected(aPresContext
, aRange
,aSelected
,eSpreadNone
);
4368 frame
= frame
->GetPrevContinuation();
4370 frame
= GetNextContinuation();
4372 frame
->SetSelected(aPresContext
, aRange
,aSelected
,eSpreadNone
);
4373 frame
= frame
->GetNextContinuation();
4380 nsTextFrame::GetPointFromOffset(nsPresContext
* aPresContext
,
4381 nsIRenderingContext
* inRendContext
,
4385 if (!aPresContext
|| !inRendContext
|| !outPoint
)
4386 return NS_ERROR_NULL_POINTER
;
4391 DEBUG_VERIFY_NOT_DIRTY(mState
);
4392 if (mState
& NS_FRAME_IS_DIRTY
)
4393 return NS_ERROR_UNEXPECTED
;
4395 if (mContentLength
<= 0) {
4399 inOffset
-=mContentOffset
;
4401 NS_ASSERTION(0,"offset less than this frame has in GetPointFromOffset");
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
)) {
4418 // Transform text from content into renderable form
4419 nsTextTransformer
tx(aPresContext
);
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
4436 nscoord width
= mRect
.width
;
4439 NS_ASSERTION(0, "invalid offset passed to GetPointFromOffset");
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
;
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
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
;
4479 outPoint
->x
= width
;
4488 nsTextFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset
,
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
;
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
);
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();
4524 return (*outChildFrame
)->GetChildFrameContainingOffset(inContentOffset
, inHint
,
4525 outFrameContentOffset
,outChildFrame
);
4527 return NS_OK
; //this can't be the right thing to do?
4530 *outFrameContentOffset
= contentOffset
;
4531 *outChildFrame
= this;
4536 nsTextFrame::PeekOffsetNoAmount(PRBool aForward
, PRInt32
* aOffset
)
4538 NS_ASSERTION (aOffset
&& *aOffset
<= mContentLength
, "aOffset out of range");
4539 return (!IsEmpty());
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
)) {
4561 nsTextTransformer
tx(presContext
);
4562 PrepareUnicodeText(tx
, &indexBuffer
, &paintBuffer
, &textLength
);
4563 PRInt32
* ip
= indexBuffer
.mBuffer
;
4565 PRBool found
= PR_TRUE
;
4568 PRUint8 selectStyle
;
4570 IsSelectable(&selectable
, &selectStyle
);
4571 if (selectStyle
== NS_STYLE_USER_SELECT_ALL
)
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
])))
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");
4603 PRBool needsCTL
= PR_FALSE
;
4604 PRInt32 previousOffset
;
4606 ctlObj
->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar
*,
4607 paintBuffer
.mBuffer
),
4608 mContentOffset
+ startOffset
, -1, &needsCTL
);
4611 ctlObj
->PrevCluster(NS_REINTERPRET_CAST(const PRUnichar
*,
4612 paintBuffer
.mBuffer
),
4613 textLength
, mContentOffset
+ startOffset
,
4615 *aOffset
= i
= previousOffset
- mContentOffset
;
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
]))) {
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");
4653 PRBool needsCTL
= PR_FALSE
;
4656 ctlObj
->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar
*,
4657 paintBuffer
.mBuffer
),
4658 mContentOffset
+ startOffset
, 0, &needsCTL
);
4662 ctlObj
->NextCluster(NS_REINTERPRET_CAST(const PRUnichar
*,
4663 paintBuffer
.mBuffer
),
4664 textLength
, mContentOffset
+ startOffset
,
4666 *aOffset
= i
= nextOffset
- mContentOffset
;
4671 if (i
> mContentLength
)
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
;
4693 PRUint8 selectStyle
;
4694 IsSelectable(&selectable
, &selectStyle
);
4695 if (selectStyle
== NS_STYLE_USER_SELECT_ALL
)
4699 tx
.Init(this, mContent
, mContentOffset
+ startOffset
);
4701 *aOffset
= 0; //initialize
4703 wordLen
= (mState
& NS_FRAME_IS_BIDI
) ? mContentOffset
: -1;
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
;
4714 wordLen
= (mState
& NS_FRAME_IS_BIDI
) ? mContentOffset
: -1;
4716 while (tx
.GetPrevWord(PR_FALSE
, &wordLen
, &contentLen
,
4717 &isWhitespace
, PR_FALSE
,
4718 aIsKeyboardSelect
)){
4719 if (aWordSelectEatSpace
? !isWhitespace
: *aSawBeforeType
)
4721 if (contentLen
>= startOffset
)
4723 *aOffset
-= contentLen
;
4724 if (aWordSelectEatSpace
? isWhitespace
: !isWhitespace
)
4725 *aSawBeforeType
= PR_TRUE
;
4727 wordLen
= (mState
& NS_FRAME_IS_BIDI
) ? mContentOffset
: -1;
4730 keepSearching
= *aOffset
<= 0;
4735 *aOffset
= mContentLength
;
4741 *aOffset
= mContentLength
; //initialize
4744 wordLen
= (mState
& NS_FRAME_IS_BIDI
) ? mContentOffset
+ mContentLength
: -1;
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
;
4755 wordLen
= (mState
& NS_FRAME_IS_BIDI
) ? mContentOffset
+ mContentLength
: -1;
4757 while (tx
.GetNextWord(PR_FALSE
, &wordLen
, &contentLen
, &isWhitespace
, &wasTransformed
, PR_TRUE
, PR_FALSE
, aIsKeyboardSelect
))
4759 if (aWordSelectEatSpace
? !isWhitespace
: *aSawBeforeType
)
4761 if (startOffset
+ contentLen
>= mContentLength
)
4763 if (aWordSelectEatSpace
? isWhitespace
: !isWhitespace
)
4764 *aSawBeforeType
= PR_TRUE
;
4765 *aOffset
+= contentLen
;
4767 wordLen
= (mState
& NS_FRAME_IS_BIDI
) ? mContentOffset
+ mContentLength
: -1;
4770 keepSearching
= *aOffset
>= mContentLength
;
4784 *aOffset
> mContentLength
||
4788 *aOffset
= PR_MIN(*aOffset
, mContentLength
);
4789 *aOffset
= PR_MAX(*aOffset
, 0);
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
;
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.
4809 if (aStartIndex
< (mContentOffset
+ mContentLength
))
4812 nsIPresShell
*shell
= aContext
->GetPresShell();
4814 return NS_ERROR_FAILURE
;
4817 nsIDocument
*doc
= shell
->GetDocument();
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
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
4844 if (start
== aEndIndex
)
4846 *aFinished
= PR_TRUE
;
4850 if (aRecurse
) //recurse through the siblings.
4852 nsIFrame
*nextInFlow
= this;
4854 while (!aFinished
&& nextInFlow
&& NS_SUCCEEDED(rv
) && !*_retval
) //while we havent found anything visible
4856 nextInFlow
= nextInFlow
->GetNextInFlow();
4859 rv
= nextInFlow
->CheckVisibility(aContext
,aStartIndex
,aEndIndex
,PR_FALSE
,aFinished
,_retval
);
4869 nsTextFrame::GetOffsets(PRInt32
&start
, PRInt32
&end
) const
4871 start
= mContentOffset
;
4872 end
= mContentOffset
+mContentLength
;
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
);}
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
];
4900 SegmentData mSegments
[TEXT_MAX_NUM_SEGMENTS
];
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");
4924 if (mNumSegments
>= TEXT_MAX_NUM_SEGMENTS
) {
4928 mTotalNumChars
+= aNumChars
;
4929 mBreaks
[mNumSegments
] = mTotalNumChars
;
4930 mSegments
[mNumSegments
].mIsWhitespace
= aIsWhitespace
;
4931 mTotalContentLen
+= aContentLen
;
4932 mSegments
[mNumSegments
].mContentLen
= PRUint32(mTotalContentLen
);
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
4987 NS_WARNING("Textframe maps no content");
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
))
5003 while (IS_DISCARDED(firstAfter
)) {
5006 if (afterLength
== 0) {
5007 // aAfter will be entirely skipped. No breaking allowed here.
5010 firstAfter
= fragAfter
->CharAt(afterOffset
);
5012 while (IS_DISCARDED(lastBefore
)) {
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");
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
))
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())
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())
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());
5051 nsTextFrame::MeasureText(nsPresContext
* aPresContext
,
5052 const nsHTMLReflowState
& aReflowState
,
5053 nsTextTransformer
& aTx
,
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) {
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()
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)*/
5105 PRUint32 estimatedNumChars
= EstimateNumChars(maxWidth
- aTextData
.mX
,
5109 nsTextFrame
* nextBidi
= nsnull
;
5110 PRInt32 start
= -1, end
;
5112 if (mState
& NS_FRAME_IS_BIDI
) {
5113 nextBidi
= NS_STATIC_CAST(nsTextFrame
*, GetLastInFlow()->GetNextContinuation());
5115 if (mContentLength
< 1) {
5118 nextBidi
->GetOffsets(start
, end
);
5119 if (start
<= mContentOffset
) {
5120 nextBidi
->AdjustOffsetsForBidi(mContentOffset
+ mContentLength
, end
);
5123 mContentLength
= start
- mContentOffset
;
5130 if (aTextData
.mMeasureText
) {
5131 aTs
.mNormalFont
->GetMaxAscent(aTextData
.mAscent
);
5132 aTs
.mNormalFont
->GetMaxDescent(aTextData
.mDescent
);
5134 PRBool firstWordDone
= PR_FALSE
;
5137 if (nextBidi
&& (mContentLength
<= 0) ) {
5138 if (textRun
.IsBuffering()) {
5139 // Measure the remaining text
5140 goto MeasureTextRun
;
5147 // Get next word/whitespace from the text
5148 PRBool isWhitespace
, wasTransformed
;
5149 PRInt32 wordLen
, contentLen
;
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
;
5171 mContentLength
-= contentLen
;
5173 if (mContentLength
< 0) {
5174 contentLen
+= mContentLength
;
5175 wordLen
= PR_MIN(wordLen
, contentLen
);
5179 // Remember if the text was transformed
5180 if (wasTransformed
) {
5181 mState
|= TEXT_WAS_TRANSFORMED
;
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
;
5191 if (textRun
.IsBuffering()) {
5192 // Measure the remaining text
5193 goto MeasureTextRun
;
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
;
5204 lastWordLen
= wordLen
;
5206 aTextData
.mInWord
= PR_FALSE
;
5208 // Measure the word/whitespace
5209 PRUnichar firstChar
;
5210 if (aTx
.TransformedTextIsAscii()) {
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
;
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
;
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
);
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
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
;
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
5294 //if we're not wrapping, then always advance
5295 // the x-offset regardless of maxWidth
5296 aTextData
.mX
+= dimensions
.width
;
5298 } //(aTextData.mMeasureText)
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
)) {
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
;
5334 if (aTs
.mSmallCaps
) {
5335 MeasureSmallCapsText(aReflowState
.rendContext
, aTs
, bp2
, wordLen
, PR_FALSE
, &dimensions
);
5338 // Measure just the one word
5339 if (aTx
.TransformedTextIsAscii()) {
5340 aReflowState
.rendContext
->GetTextDimensions(bp1
, wordLen
, dimensions
);
5342 aReflowState
.rendContext
->GetTextDimensions(bp2
, wordLen
, dimensions
);
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
) {
5350 nsBoundingMetrics bm
;
5351 if (aTx
.TransformedTextIsAscii()) {
5352 res
= aReflowState
.rendContext
->GetBoundingMetrics(bp1
, wordLen
, bm
);
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
;
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
;
5373 for (PRUnichar
* bp
= bp2
; bp
< bp2
+ wordLen
; bp
++) {
5374 if (*bp
== ' ') // || *bp == CH_CJKSP)
5375 dimensions
.width
+= aTs
.mWordSpacing
;
5381 lastWordDimensions
= dimensions
;
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.
5392 canBreak
= aTextData
.mWrapping
;
5395 // Remember that we *could* have broken here, even if we choose not to
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.
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
;
5416 endsInWhitespace
= PR_FALSE
;
5417 aTextData
.mOffset
+= contentLen
;
5418 if (justDidFirstLetter
) {
5425 // Remember that we *could* have broken before this chunk of text.
5427 if (aTextData
.mOffset
== startingOffset
) {
5428 canBreak
= canBreakBetweenTextFrames
;
5430 canBreak
= aTextData
.mWrapping
;
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
5443 endsInWhitespace
= PR_FALSE
;
5444 aTextData
.mOffset
+= contentLen
;
5445 if (justDidFirstLetter
) {
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");
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
);
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
5491 if (0 == aTextData
.mX
) {
5492 canBreak
= canBreakBetweenTextFrames
;
5494 canBreak
= aTextData
.mWrapping
;
5496 if (canBreak
&& aTextData
.mX
+ dimensions
.width
> maxWidth
) {
5497 // None of the text fits
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;
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!
5524 /* O'Callahan XXX: This snippet together with the snippet above prevents mail from loading
5526 if (lastSegment < 0) {
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(),
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
) {
5575 if (nextBidi
&& (mContentLength
<= 0) ) {
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
;
5587 // Reset the number of text run segments
5590 // Estimate the remaining number of characters we think will fit
5591 estimatedNumChars
= EstimateNumChars(maxWidth
- aTextData
.mX
,
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
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
);
5624 // Don't allow subsequent text frame to break-before. All our text is
5625 // being skipped (usually whitespace, could be discarded Unicode control
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
)
5639 || (aTextData
.mOffset
== start
)
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();
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.
5690 nsTextFrame::AddInlineMinWidth(nsIRenderingContext
*aRenderingContext
,
5691 nsIFrame::InlineMinWidthData
*aData
)
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.
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
) ||
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
,
5717 if (NS_FAILED(rv
)) {
5718 NS_NOTREACHED("failure initializing text transformer");
5722 if (aData
->trailingTextFrame
&&
5723 CanBreakBetween(NS_STATIC_CAST(nsTextFrame
*, aData
->trailingTextFrame
),
5724 aData
->trailingTextFrame
->
5725 GetStyleText()->WhiteSpaceCanWrap(),
5727 aData
->skipWhitespace
, // XXX ???
5728 nsnull
)) // XXX Better to pass real frame
5730 aData
->OptionallyBreak(aRenderingContext
);
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
);
5746 // XXX Watch mContentLength!
5749 PRUnichar firstChar
;
5750 if (tx
.TransformedTextIsAscii()) {
5755 if ('\n' == firstChar
) {
5756 aData
->ForceBreak(aRenderingContext
);
5757 aData
->skipWhitespace
= PR_TRUE
;
5758 aData
->trailingWhitespace
= 0;
5759 } else if (!aData
->skipWhitespace
|| wsSignificant
) {
5762 if ('\t' == firstChar
) {
5763 // XXX Need to track column!
5765 // Apply word spacing to every space derived from a tab
5767 (ts
.mSpaceWidth
+ ts
.mWordSpacing
+ ts
.mLetterSpacing
)*wordLen
;
5769 // Apply word spacing to every space, if there's more than one
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
;
5779 aData
->trailingWhitespace
+= width
;
5780 aData
->skipWhitespace
= PR_TRUE
;
5784 aData
->OptionallyBreak(aRenderingContext
);
5788 if (!atStart
&& wrapping
) {
5789 aData
->OptionallyBreak(aRenderingContext
);
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
,
5803 width
= dimensions
.width
;
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
);
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
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;
5832 nsTextFrame::AddInlinePrefWidth(nsIRenderingContext
*aRenderingContext
,
5833 nsIFrame::InlinePrefWidthData
*aData
)
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.
5844 PRBool forceArabicShaping
= (ts
.mSmallCaps
||
5845 (0 != ts
.mWordSpacing
) ||
5846 (0 != ts
.mLetterSpacing
) ||
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
,
5854 if (NS_FAILED(rv
)) {
5855 NS_NOTREACHED("failure initializing text transformer");
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
);
5873 // XXX Watch mContentLength!
5876 PRUnichar firstChar
;
5877 if (tx
.TransformedTextIsAscii()) {
5882 if ('\n' == firstChar
) {
5883 aData
->ForceBreak(aRenderingContext
);
5884 } else if (!aData
->skipWhitespace
) {
5886 if ('\t' == firstChar
) {
5887 // XXX Need to track column!
5889 // Apply word spacing to every space derived from a tab
5891 (ts
.mSpaceWidth
+ ts
.mWordSpacing
+ ts
.mLetterSpacing
)*wordLen
;
5893 // Apply word spacing to every space, if there's more than one
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;
5903 aData
->trailingWhitespace
+= 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
,
5915 width
= dimensions
.width
;
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
);
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
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
,
5947 // Inlines and text don't compute size before reflow.
5948 return nsSize(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
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
);
5961 printf(": BeginReflow: availableSize=%d,%d\n",
5962 aReflowState
.availableWidth
, aReflowState
.availableHeight
);
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
5973 aMetrics
.height
= 0;
5974 aMetrics
.ascent
= 0;
5976 if (NS_REFLOW_CALC_BOUNDING_METRICS
& aMetrics
.mFlags
)
5977 aMetrics
.mBoundingMetrics
.Clear();
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
;
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);
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();
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
) ||
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
);
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();
6071 mState
|= TEXT_IN_WORD
;
6073 mState
&= ~TEXT_FIRST_LETTER
;
6075 PRInt32 column
= lineLayout
.GetColumn();
6076 PRInt32 prevColumn
= mColumn
;
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
6086 // - we aren't computing the max element size (that requires we measure
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
))) &&
6106 (0 == (mState
& NS_FRAME_IS_BIDI
) ) &&
6109 // We can skip measuring of text and use the value from our
6111 measureText
= PR_FALSE
;
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
);
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
);
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
);
6133 textData
.mAscent
= 0;
6134 textData
.mDescent
= 0;
6135 aStatus
= NS_FRAME_COMPLETE
;
6137 if (textData
.mTrailingSpaceTrimmed
)
6138 mState
|= TEXT_TRIMMED_WS
;
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;
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
;
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
;
6176 if (ts
.mJustifying
|| calcMathMLMetrics
) {
6177 PRIntn numJustifiableCharacter
;
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
);
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
;
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
;
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
;
6224 mState
&= ~TEXT_OPTIMIZE_RESIZE
;
6227 // If it's an incremental reflow command, then invalidate our existing
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
);
6249 printf(": desiredSize=%d,%d(b=%d) status=%x\n",
6250 aMetrics
.width
, aMetrics
.height
, aMetrics
.ascent
,
6253 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aMetrics
);
6257 /* virtual */ PRBool
6258 nsTextFrame::CanContinueTextRun() const
6260 // We can continue a text run through a text frame
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
) {
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();
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
6296 nsLayoutUtils::SetFontFromStyle(&aRC
, mStyleContext
);
6298 aRC
.GetWidth(' ', dw
);
6299 // NOTE: Trailing whitespace includes word and letter spacing!
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
;
6318 printf(": trim => %d\n", dw
);
6321 mState
|= TEXT_TRIMMED_WS
;
6324 mState
&= ~TEXT_TRIMMED_WS
;
6331 // Translate the mapped content into a string that's printable
6333 nsTextFrame::ToCString(nsString
& aBuf
, PRInt32
* aTotalContentLength
) const
6335 // Get the frames text content
6336 const nsTextFragment
* frag
= mContent
->GetText();
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
) {
6348 PRInt32 fragOffset
= mContentOffset
;
6349 PRInt32 n
= fragOffset
+ mContentLength
;
6350 while (fragOffset
< n
) {
6351 PRUnichar ch
= frag
->CharAt(fragOffset
++);
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);
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
),
6381 // XXXldb Should this check compatibility mode as well???
6382 if (GetStyleText()->WhiteSpaceIsSignificant()) {
6386 if (mState
& TEXT_ISNOT_ONLY_WHITESPACE
) {
6390 if (mState
& TEXT_IS_ONLY_WHITESPACE
) {
6394 PRBool isEmpty
= mContent
->TextIsOnlyWhitespace();
6395 mState
|= (isEmpty
? TEXT_IS_ONLY_WHITESPACE
: TEXT_ISNOT_ONLY_WHITESPACE
);
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
);
6415 nsTextFrame::List(FILE* out
, PRInt32 aIndent
) const
6418 IndentBy(out
, aIndent
);
6420 #ifdef DEBUG_waterson
6421 fprintf(out
, " [parent=%p]", mParent
);
6424 fprintf(out
, " [view=%p]", NS_STATIC_CAST(void*, GetView()));
6427 PRInt32 totalContentLength
;
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
);
6451 if (mState
& NS_FRAME_SELECTED_CONTENT
) {
6452 fprintf(out
, " [state=%08x] SELECTED", mState
);
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();
6461 nsAutoString atomString
;
6462 pseudoTag
->ToString(atomString
);
6463 fprintf(out
, " pst=%s",
6464 NS_LossyConvertUTF16toASCII(atomString
).get());
6471 IndentBy(out
, aIndent
);
6473 fputs(NS_LossyConvertUTF16toASCII(tmp
).get(), out
);
6477 IndentBy(out
, aIndent
);
6484 void nsTextFrame::AdjustSelectionPointsForBidi(SelectionDetails
*sdptr
,
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) ||
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 )
6525 NS_ASSERTION((sdptr
->mEnd
>= 0) , "mEnd >= 0");
6526 if(sdptr
->mEnd
< 0 )
6529 NS_ASSERTION((sdptr
->mStart
<= sdptr
->mEnd
), "mStart <= mEnd");
6530 if(sdptr
->mStart
> sdptr
->mEnd
)
6531 sdptr
->mEnd
= sdptr
->mStart
;
6538 nsTextFrame::AdjustOffsetsForBidi(PRInt32 aStart
, PRInt32 aEnd
)
6540 AddStateBits(NS_FRAME_IS_BIDI
);
6541 SetOffsets(aStart
, aEnd
);
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.
6556 nsTextFrame::HasTerminalNewline() const
6558 const nsTextFragment
* frag
= mContent
->GetText();
6559 if (frag
&& mContentLength
> 0) {
6560 PRUnichar ch
= frag
->CharAt(mContentOffset
+ mContentLength
- 1);