From 02e063297c96e73d361c5f6eb0f81670ca72d047 Mon Sep 17 00:00:00 2001 From: "roc+@cs.cmu.edu" Date: Sun, 1 Jul 2007 20:31:57 -0700 Subject: [PATCH] Removing nsTextFrame.cpp and associated makefile rules. r/sr=pavlov for the removal. --- layout/generic/Makefile.in | 18 +- layout/generic/nsTextFrame.cpp | 6565 -------------------------------- 2 files changed, 3 insertions(+), 6580 deletions(-) delete mode 100644 layout/generic/nsTextFrame.cpp diff --git a/layout/generic/Makefile.in b/layout/generic/Makefile.in index 58eea260d91e..90558bfc3b0d 100644 --- a/layout/generic/Makefile.in +++ b/layout/generic/Makefile.in @@ -158,6 +158,9 @@ CPPSRCS = \ nsSpaceManager.cpp \ nsSpacerFrame.cpp \ nsSplittableFrame.cpp \ + nsTextFrameThebes.cpp \ + nsTextFrameUtils.cpp \ + nsTextRunTransformations.cpp \ nsTextTransformer.cpp \ nsViewportFrame.cpp \ $(NULL) @@ -168,21 +171,6 @@ CPPSRCS += \ $(NULL) endif -# set this to 1 to enable the new text frame -MOZ_ENABLE_NEW_TEXT_FRAME ?= 1 - -ifdef MOZ_ENABLE_NEW_TEXT_FRAME -CPPSRCS += \ - nsTextFrameThebes.cpp \ - nsTextRunTransformations.cpp \ - nsTextFrameUtils.cpp \ - $(NULL) -else -CPPSRCS += \ - nsTextFrame.cpp \ - $(NULL) -endif - RESOURCES_HTML = \ $(srcdir)/gopher-audio.gif \ $(srcdir)/gopher-binary.gif \ diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp deleted file mode 100644 index 762ee71f8aba..000000000000 --- a/layout/generic/nsTextFrame.cpp +++ /dev/null @@ -1,6565 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Robert O'Callahan - * Roger B. Sidje - * Pierre Phaneuf - * Prabhat Hegde - * Tomi Leppikangas - * Roland Mainz - * Daniel Glazman - * Neil Deakin - * Masayuki Nakano - * Mats Palmgren - * Uri Bernstein - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* rendering object for textual content of elements */ - -#include "nsCOMPtr.h" -#include "nsHTMLParts.h" -#include "nsCRT.h" -#include "nsSplittableFrame.h" -#include "nsLineLayout.h" -#include "nsString.h" -#include "nsUnicharUtils.h" -#include "nsPresContext.h" -#include "nsIContent.h" -#include "nsStyleConsts.h" -#include "nsStyleContext.h" -#include "nsCoord.h" -#include "nsIFontMetrics.h" -#include "nsIRenderingContext.h" -#include "nsIPresShell.h" -#include "nsITimer.h" -#include "prtime.h" -#include "nsVoidArray.h" -#include "prprf.h" -#include "nsIDOMText.h" -#include "nsIDocument.h" -#include "nsIDeviceContext.h" -#include "nsICaret.h" -#include "nsCSSPseudoElements.h" -#include "nsILineBreaker.h" -#include "nsCompatibility.h" -#include "nsCSSColorUtils.h" -#include "nsLayoutUtils.h" -#include "nsDisplayList.h" -#include "nsFrame.h" -#include "nsTextTransformer.h" - -#include "nsTextFragment.h" -#include "nsGkAtoms.h" -#include "nsFrameSelection.h" -#include "nsISelection.h" -#include "nsIDOMRange.h" -#include "nsILookAndFeel.h" -#include "nsCSSRendering.h" -#include "nsContentUtils.h" - -#include "nsILineIterator.h" - -#include "nsCompressedCharMap.h" - -#include "nsIServiceManager.h" -#ifdef ACCESSIBILITY -#include "nsIAccessible.h" -#include "nsIAccessibilityService.h" -#endif -#include "nsGUIEvent.h" -#include "nsAutoPtr.h" -#include "nsStyleSet.h" - -#include "nsBidiFrames.h" -#include "nsBidiPresUtils.h" -#include "nsBidiUtils.h" -#include "nsTextFrameTextRunCache.h" - -nsresult -nsTextFrameTextRunCache::Init() { - return NS_OK; -} - -void -nsTextFrameTextRunCache::Shutdown() { -} - -#ifdef SUNCTL -#include "nsILE.h" -static NS_DEFINE_CID(kLECID, NS_ULE_CID); -#endif /* SUNCTL */ - -#ifdef NS_DEBUG -#undef NOISY_BLINK -#undef DEBUG_WORD_WRAPPING -#undef NOISY_REFLOW -#undef NOISY_TRIM -#else -#undef NOISY_BLINK -#undef DEBUG_WORD_WRAPPING -#undef NOISY_REFLOW -#undef NOISY_TRIM -#endif - -// #define DEBUGWORDJUMP - -#define kSZLIG 0x00DF -//---------------------------------------------------------------------- - -#define TEXT_BUF_SIZE 100 - -//---------------------------------------- - -struct nsAutoIndexBuffer; -struct nsAutoPRUint8Buffer; - -class nsTextStyle { -public: - const nsStyleFont* mFont; - const nsStyleText* mText; - nsIFontMetrics* mNormalFont; - nsIFontMetrics* mSmallFont; - nsIFontMetrics* mLastFont; - PRBool mSmallCaps; - nscoord mWordSpacing; - nscoord mLetterSpacing; - nscoord mSpaceWidth; - nscoord mAveCharWidth; - PRBool mJustifying; - PRBool mPreformatted; - PRInt32 mNumJustifiableCharacterToRender; - PRInt32 mNumJustifiableCharacterToMeasure; - nscoord mExtraSpacePerJustifiableCharacter; - PRInt32 mNumJustifiableCharacterReceivingExtraJot; - - nsTextStyle(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* sc); - - ~nsTextStyle(); -}; - -// Contains extra style data needed only for painting (not reflowing) -class nsTextPaintStyle : public nsTextStyle { -public: - enum{ - eNormalSelection = - nsISelectionController::SELECTION_NORMAL, - eIMESelections = - nsISelectionController::SELECTION_IME_RAWINPUT | - nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT | - nsISelectionController::SELECTION_IME_CONVERTEDTEXT | - nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT, - eAllSelections = - eNormalSelection | eIMESelections - }; - - const nsStyleColor* mColor; - - nsTextPaintStyle(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsIContent* aContent, - PRInt16 aSelectionStatus); - ~nsTextPaintStyle(); - - nscolor GetTextColor(); - void GetSelectionColors(nscolor* aForeColor, - nscolor* aBackColor, - PRBool* aBackIsTransparent); - void GetIMESelectionColors(SelectionType aSelectionType, - nscolor* aForeColor, - nscolor* aBackColor, - PRBool* aBackIsTransparent); - // if this returns PR_FALSE, we don't need to draw underline. - PRBool GetIMEUnderline(SelectionType aSelectionType, - nscolor* aLineColor, - float* aRelativeSize); -protected: - nsPresContext* mPresContext; - nsStyleContext* mStyleContext; - nsIContent* mContent; - PRInt16 mSelectionStatus; // see nsIDocument.h SetDisplaySelection() - - // Common colors - PRBool mInitCommonColors; - - PRInt32 mSufficientContrast; - nscolor mFrameBackgroundColor; - - // Selection colors - PRBool mInitSelectionColors; - - nscolor mSelectionTextColor; - nscolor mSelectionBGColor; - PRBool mSelectionBGIsTransparent; - - // IME selection colors and underline info - struct nsIMEColor { - PRBool mInit; - nscolor mTextColor; - nscolor mBGColor; - nscolor mBGIsTransparent; - nscolor mUnderlineColor; - }; - nsIMEColor mIMEColor[4]; - // indexs - enum { - eIndexRawInput = 0, - eIndexSelRawText, - eIndexConvText, - eIndexSelConvText - }; - float mIMEUnderlineRelativeSize; - - // Color initializations - PRBool InitCommonColors(); - PRBool InitSelectionColors(); - - nsIMEColor* GetIMEColor(SelectionType aSelectionType); - PRBool InitIMEColors(SelectionType aSelectionType, nsIMEColor*); - - PRBool EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor); - - nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor, - nscolor aBackColor); -}; - -class nsTextFrame : public nsFrame { -public: - nsTextFrame(nsStyleContext* aContext) : nsFrame(aContext) - { - NS_ASSERTION(mContentOffset == 0, "Bogus content offset"); - NS_ASSERTION(mContentLength == 0, "Bogus content length"); - } - - // nsIFrame - NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, - const nsRect& aDirtyRect, - const nsDisplayListSet& aLists); - - void PaintText(nsIRenderingContext& aRenderingContext, nsPoint aPt); - - NS_IMETHOD Init(nsIContent* aContent, - nsIFrame* aParent, - nsIFrame* aPrevInFlow); - - virtual void Destroy(); - - NS_IMETHOD GetCursor(const nsPoint& aPoint, - nsIFrame::Cursor& aCursor); - - NS_IMETHOD CharacterDataChanged(nsPresContext* aPresContext, - nsIContent* aChild, - PRBool aAppend); - - virtual nsIFrame* GetNextContinuation() const { - return mNextContinuation; - } - NS_IMETHOD SetNextContinuation(nsIFrame* aNextContinuation) { - NS_ASSERTION (!aNextContinuation || GetType() == aNextContinuation->GetType(), - "setting a next continuation with incorrect type!"); - NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextContinuation, this), - "creating a loop in continuation chain!"); - mNextContinuation = aNextContinuation; - if (aNextContinuation) - aNextContinuation->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION); - return NS_OK; - } - virtual nsIFrame* GetNextInFlowVirtual() const { return GetNextInFlow(); } - nsIFrame* GetNextInFlow() const { - return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? - mNextContinuation : nsnull; - } - NS_IMETHOD SetNextInFlow(nsIFrame* aNextInFlow) { - NS_ASSERTION (!aNextInFlow || GetType() == aNextInFlow->GetType(), - "setting a next in flow with incorrect type!"); - NS_ASSERTION (!nsSplittableFrame::IsInNextContinuationChain(aNextInFlow, this), - "creating a loop in continuation chain!"); - mNextContinuation = aNextInFlow; - if (aNextInFlow) - aNextInFlow->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION); - return NS_OK; - } - virtual nsIFrame* GetLastInFlow() const; - virtual nsIFrame* GetLastContinuation() const; - - virtual nsSplittableType GetSplittableType() const { - return NS_FRAME_SPLITTABLE; - } - - /** - * Get the "type" of the frame - * - * @see nsGkAtoms::textFrame - */ - virtual nsIAtom* GetType() const; - - virtual PRBool IsFrameOfType(PRUint32 aFlags) const - { - // Set the frame state bit for text frames to mark them as replaced. - // XXX kipp: temporary - return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eReplaced | - nsIFrame::eLineParticipant)); - } - -#ifdef DEBUG - NS_IMETHOD List(FILE* out, PRInt32 aIndent) const; - NS_IMETHOD GetFrameName(nsAString& aResult) const; - NS_IMETHOD_(nsFrameState) GetDebugStateBits() const ; -#endif - - NS_IMETHOD GetPositionHelper(const nsPoint& aPoint, - nsIContent ** aNewContent, - PRInt32& aContentOffset, - PRInt32& aContentOffsetEnd); - - virtual ContentOffsets CalcContentOffsetsFromFramePoint(nsPoint aPoint); - - NS_IMETHOD GetPositionSlowly(nsIRenderingContext * aRendContext, - const nsPoint& aPoint, - nsIContent ** aNewContent, - PRInt32& aOffset); - - - NS_IMETHOD SetSelected(nsPresContext* aPresContext, - nsIDOMRange *aRange, - PRBool aSelected, - nsSpread aSpread); - - virtual PRBool PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset); - virtual PRBool PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset); - virtual PRBool PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType); - - NS_IMETHOD CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval); - - NS_IMETHOD GetOffsets(PRInt32 &start, PRInt32 &end)const; - - virtual void AdjustOffsetsForBidi(PRInt32 start, PRInt32 end); - - NS_IMETHOD GetPointFromOffset(nsPresContext* inPresContext, - nsIRenderingContext* inRendContext, - PRInt32 inOffset, - nsPoint* outPoint); - - NS_IMETHOD GetChildFrameContainingOffset(PRInt32 inContentOffset, - PRBool inHint, - PRInt32* outFrameContentOffset, - nsIFrame* *outChildFrame); - - virtual PRBool IsVisibleInSelection(nsISelection* aSelection); - - virtual PRBool IsEmpty(); - virtual PRBool IsSelfEmpty() { return IsEmpty(); } - - /** - * @return PR_TRUE if this text frame ends with a newline character. It - * should return PR_FALSE if this is not a text frame. - */ - virtual PRBool HasTerminalNewline() const; - -#ifdef ACCESSIBILITY - NS_IMETHOD GetAccessible(nsIAccessible** aAccessible); -#endif - - // nsIHTMLReflow - virtual void MarkIntrinsicWidthsDirty(); - virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext); - virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext); - virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext, - InlineMinWidthData *aData); - virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext, - InlinePrefWidthData *aData); - virtual nsSize ComputeSize(nsIRenderingContext *aRenderingContext, - nsSize aCBSize, nscoord aAvailableWidth, - nsSize aMargin, nsSize aBorder, nsSize aPadding, - PRBool aShrinkWrap); - NS_IMETHOD Reflow(nsPresContext* aPresContext, - nsHTMLReflowMetrics& aMetrics, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus); - virtual PRBool CanContinueTextRun() const; - NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext, - nsIRenderingContext& aRC, - nscoord& aDeltaWidth, - PRBool& aLastCharIsJustifiable); - - struct TextReflowData { - PRInt32 mX; // OUT - PRInt32 mOffset; // IN/OUT How far along we are in the content - nscoord mAscent; // OUT - nscoord mDescent; // OUT - PRPackedBool mWrapping; // IN - PRPackedBool mSkipWhitespace; // IN - PRPackedBool mMeasureText; // IN - PRPackedBool mInWord; // IN - PRPackedBool mFirstLetterOK; // IN - PRPackedBool mCanBreakBefore; // IN - PRPackedBool mTrailingSpaceTrimmed; // IN/OUT - - TextReflowData(PRInt32 aStartingOffset, - PRBool aWrapping, - PRBool aSkipWhitespace, - PRBool aMeasureText, - PRBool aInWord, - PRBool aFirstLetterOK, - PRBool aCanBreakBefore, - PRBool aTrailingSpaceTrimmed) - : mX(0), - mOffset(aStartingOffset), - mAscent(0), - mDescent(0), - mWrapping(aWrapping), - mSkipWhitespace(aSkipWhitespace), - mMeasureText(aMeasureText), - mInWord(aInWord), - mFirstLetterOK(aFirstLetterOK), - mCanBreakBefore(aCanBreakBefore), - mTrailingSpaceTrimmed(aTrailingSpaceTrimmed) - {} - }; - - nsIDocument* GetDocument(nsPresContext* aPresContext); - - void PrepareUnicodeText(nsTextTransformer& aTransformer, - nsAutoIndexBuffer* aIndexBuffer, - nsAutoTextBuffer* aTextBuffer, - PRInt32* aTextLen, - PRBool aForceArabicShaping = PR_FALSE, - PRIntn* aJustifiableCharCount = nsnull, - PRBool aRemoveMultipleTrimmedWS = PR_FALSE); - void ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext, - nsTextStyle& aTextStyle, - PRUnichar* aBuffer, PRInt32 aLength, PRInt32 aNumJustifiableCharacter); - - void SetupTextRunDirection(nsPresContext* aPresContext, nsIRenderingContext* aRenderingContext); - - /** - * @param aRightToLeftText whether the rendering context is reversing text - * using its native right-to-left capability - */ - void PaintTextDecorations(nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsPresContext* aPresContext, - nsTextPaintStyle& aStyle, - nscoord aX, nscoord aY, nscoord aWidth, - PRBool aRightToLeftText, - PRUnichar* aText = nsnull, - SelectionDetails *aDetails = nsnull, - PRUint32 aIndex = 0, - PRUint32 aLength = 0, - const nscoord* aSpacing = nsnull); - - void PaintTextSlowly(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsTextPaintStyle& aStyle, - nscoord aX, nscoord aY); - - // The passed-in rendering context must have its color set to the color the - // text should be rendered in. - /** - * @param aRightToLeftText whether the rendering context is reversing text - * using its native right-to-left capability - */ - void RenderString(nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsPresContext* aPresContext, - nsTextPaintStyle& aStyle, - PRBool aRightToLeftText, - PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame, - nscoord aX, nscoord aY, - nscoord aWidth, - SelectionDetails *aDetails = nsnull); - - void MeasureSmallCapsText(nsIRenderingContext* aRenderingContext, - nsTextStyle& aStyle, - PRUnichar* aWord, - PRInt32 aWordLength, - PRBool aIsEndOfFrame, - nsTextDimensions* aDimensionsResult); - - PRUint32 EstimateNumChars(PRUint32 aAvailableWidth, - PRUint32 aAverageCharWidth); - - nsReflowStatus MeasureText(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsTextTransformer& aTx, - nsTextStyle& aTs, - TextReflowData& aTextData); - - void GetTextDimensions(nsIRenderingContext& aRenderingContext, - nsTextStyle& aStyle, - PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame, - nsTextDimensions* aDimensionsResult); - - //this returns the index into the PAINTBUFFER of the x coord aWidth(based on 0 as far left) - //also note: this is NOT added to mContentOffset since that would imply that this return is - //meaningful to content yet. use index buffer from prepareunicodestring to find the content offset. - PRInt32 GetLengthSlowly(nsIRenderingContext& aRenderingContext, - nsTextStyle& aStyle, - PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame, - nscoord aWidth); - - // REVIEW: There is absolute no reason why IsTextInSelection should depend - // on a rendering context, and I've refactored the code so it doesn't. - PRBool IsTextInSelection(); - - nsresult GetTextInfoForPainting(nsPresContext* aPresContext, - nsIPresShell** aPresShell, - nsISelectionController** aSelectionController, - PRBool& aDisplayingSelection, - PRBool& aIsPaginated, - PRBool& aIsSelected, - PRBool& aHideStandardSelection, - PRInt16& aSelectionValue); - - nsresult GetSelectionStatus(nsPresContext* aPresContext, - PRInt16& aSelectionValue); - - void PaintUnicodeText(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsTextPaintStyle& aStyle, - nscoord dx, nscoord dy); - - void PaintAsciiText(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsTextPaintStyle& aStyle, - nscoord dx, nscoord dy); - -#ifdef DEBUG - void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const; -#endif - - PRInt32 GetContentOffset() { return mContentOffset; } - PRInt32 GetContentLength() { return mContentLength; } - -protected: - virtual ~nsTextFrame(); - - nsIFrame* mNextContinuation; - PRInt32 mContentOffset; - PRInt32 mContentLength; - PRInt32 mColumn; - nscoord mAscent; - //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 - PRInt32 GetTextDimensionsOrLength(nsIRenderingContext& aRenderingContext, - nsTextStyle& aStyle, - PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame, - nsTextDimensions* aDimensionsResult, - PRBool aGetTextDimensions/* true=get dimensions false = return length up to aDimensionsResult->width size*/); - nsresult GetContentAndOffsetsForSelection(nsPresContext* aPresContext,nsIContent **aContent, PRInt32 *aOffset, PRInt32 *aLength); - - void AdjustSelectionPointsForBidi(SelectionDetails *sdptr, - PRInt32 textLength, - PRBool isRTLChars, - PRBool isOddLevel, - PRBool isBidiSystem); - - void SetOffsets(PRInt32 start, PRInt32 end); - - PRBool IsChineseJapaneseLangGroup(); - PRBool IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ); - - nsresult FillClusterBuffer(nsPresContext *aPresContext, const PRUnichar *aText, - PRUint32 aLength, nsAutoPRUint8Buffer& aClusterBuffer); -}; - -//---------------------------------------- - -// checks to see if the text can be lightened.. -// text is darkend -inline PRBool CanDarken(nsPresContext* aPresContext) -{ - PRBool darken; - - if (aPresContext->GetBackgroundColorDraw()) { - darken = PR_FALSE; - } else { - if (aPresContext->GetBackgroundImageDraw()) { - darken = PR_FALSE; - } else { - darken = PR_TRUE; - } - } - - return darken; -} - - -struct nsAutoIndexBuffer { - nsAutoIndexBuffer(); - ~nsAutoIndexBuffer(); - - nsresult GrowTo(PRInt32 aAtLeast); - - PRInt32* mBuffer; - PRInt32 mBufferLen; - PRInt32 mAutoBuffer[TEXT_BUF_SIZE]; -}; - -nsAutoIndexBuffer::nsAutoIndexBuffer() - : mBuffer(mAutoBuffer), - mBufferLen(TEXT_BUF_SIZE) -{ -#ifdef DEBUG - memset(mAutoBuffer, 0xdd, sizeof(mAutoBuffer)); -#endif -} - -nsAutoIndexBuffer::~nsAutoIndexBuffer() -{ - if (mBuffer && (mBuffer != mAutoBuffer)) { - delete [] mBuffer; - } -} - -nsresult -nsAutoIndexBuffer::GrowTo(PRInt32 aAtLeast) -{ - if (aAtLeast > mBufferLen) - { - PRInt32 newSize = mBufferLen * 2; - if (newSize < mBufferLen + aAtLeast) { - newSize = mBufferLen * 2 + aAtLeast; - } - PRInt32* newBuffer = new PRInt32[newSize]; - if (!newBuffer) { - return NS_ERROR_OUT_OF_MEMORY; - } -#ifdef DEBUG - memset(newBuffer, 0xdd, sizeof(PRInt32) * newSize); -#endif - memcpy(newBuffer, mBuffer, sizeof(PRInt32) * mBufferLen); - if (mBuffer != mAutoBuffer) { - delete [] mBuffer; - } - mBuffer = newBuffer; - mBufferLen = newSize; - } - return NS_OK; -} - -struct nsAutoPRUint8Buffer { - nsAutoPRUint8Buffer(); - ~nsAutoPRUint8Buffer(); - - nsresult GrowTo(PRInt32 aAtLeast); - - PRUint8* mBuffer; - PRInt32 mBufferLen; - PRUint8 mAutoBuffer[TEXT_BUF_SIZE]; -}; - -nsAutoPRUint8Buffer::nsAutoPRUint8Buffer() - : mBuffer(mAutoBuffer), - mBufferLen(TEXT_BUF_SIZE) -{ -#ifdef DEBUG - memset(mAutoBuffer, 0xdd, sizeof(mAutoBuffer)); -#endif -} - -nsAutoPRUint8Buffer::~nsAutoPRUint8Buffer() -{ - if (mBuffer && (mBuffer != mAutoBuffer)) { - delete [] mBuffer; - } -} - -nsresult -nsAutoPRUint8Buffer::GrowTo(PRInt32 aAtLeast) -{ - if (aAtLeast > mBufferLen) - { - PRInt32 newSize = mBufferLen * 2; - if (newSize < mBufferLen + aAtLeast) { - newSize = mBufferLen * 2 + aAtLeast; - } - PRUint8* newBuffer = new PRUint8[newSize]; - if (!newBuffer) { - return NS_ERROR_OUT_OF_MEMORY; - } -#ifdef DEBUG - memset(newBuffer, 0xdd, sizeof(PRUint8) * newSize); -#endif - memcpy(newBuffer, mBuffer, sizeof(PRUint8) * mBufferLen); - if (mBuffer != mAutoBuffer) { - delete [] mBuffer; - } - mBuffer = newBuffer; - mBufferLen = newSize; - } - return NS_OK; -} - - -//---------------------------------------------------------------------- - -// Helper class for managing blinking text - -class nsBlinkTimer : public nsITimerCallback -{ -public: - nsBlinkTimer(); - virtual ~nsBlinkTimer(); - - NS_DECL_ISUPPORTS - - void AddFrame(nsIFrame* aFrame); - - PRBool RemoveFrame(nsIFrame* aFrame); - - PRInt32 FrameCount(); - - void Start(); - - void Stop(); - - NS_DECL_NSITIMERCALLBACK - - static nsresult AddBlinkFrame(nsPresContext* aPresContext, nsIFrame* aFrame); - static nsresult RemoveBlinkFrame(nsIFrame* aFrame); - - static PRBool GetBlinkIsOff() { return sState == 3; } - -protected: - - nsCOMPtr mTimer; - nsVoidArray mFrames; - -protected: - - static nsBlinkTimer* sTextBlinker; - static PRUint32 sState; // 0-2 == on; 3 == off - -}; - -nsBlinkTimer* nsBlinkTimer::sTextBlinker = nsnull; -PRUint32 nsBlinkTimer::sState = 0; - -#ifdef NOISY_BLINK -static PRTime gLastTick; -#endif - -nsBlinkTimer::nsBlinkTimer() -{ -} - -nsBlinkTimer::~nsBlinkTimer() -{ - Stop(); - sTextBlinker = nsnull; -} - -void nsBlinkTimer::Start() -{ - nsresult rv; - mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); - if (NS_OK == rv) { - mTimer->InitWithCallback(this, 250, nsITimer::TYPE_REPEATING_PRECISE); - } -} - -void nsBlinkTimer::Stop() -{ - if (nsnull != mTimer) { - mTimer->Cancel(); - } -} - -NS_IMPL_ISUPPORTS1(nsBlinkTimer, nsITimerCallback) - -void nsBlinkTimer::AddFrame(nsIFrame* aFrame) { - mFrames.AppendElement(aFrame); - if (1 == mFrames.Count()) { - Start(); - } -} - -PRBool nsBlinkTimer::RemoveFrame(nsIFrame* aFrame) { - PRBool rv = mFrames.RemoveElement(aFrame); - if (0 == mFrames.Count()) { - Stop(); - } - return rv; -} - -PRInt32 nsBlinkTimer::FrameCount() { - return mFrames.Count(); -} - -NS_IMETHODIMP nsBlinkTimer::Notify(nsITimer *timer) -{ - // Toggle blink state bit so that text code knows whether or not to - // render. All text code shares the same flag so that they all blink - // in unison. - sState = (sState + 1) % 4; - if (sState == 1 || sState == 2) - // States 0, 1, and 2 are all the same. - return NS_OK; - -#ifdef NOISY_BLINK - PRTime now = PR_Now(); - char buf[50]; - PRTime delta; - LL_SUB(delta, now, gLastTick); - gLastTick = now; - PR_snprintf(buf, sizeof(buf), "%lldusec", delta); - printf("%s\n", buf); -#endif - - PRInt32 i, n = mFrames.Count(); - for (i = 0; i < n; i++) { - nsIFrame* frame = (nsIFrame*) mFrames.ElementAt(i); - - // Determine damaged area and tell view manager to redraw it - // blink doesn't blink outline ... I hope - nsRect bounds(nsPoint(0, 0), frame->GetSize()); - frame->Invalidate(bounds, PR_FALSE); - } - return NS_OK; -} - - -// static -nsresult nsBlinkTimer::AddBlinkFrame(nsPresContext* aPresContext, nsIFrame* aFrame) -{ - if (!sTextBlinker) - { - sTextBlinker = new nsBlinkTimer; - if (!sTextBlinker) return NS_ERROR_OUT_OF_MEMORY; - } - - NS_ADDREF(sTextBlinker); - - sTextBlinker->AddFrame(aFrame); - return NS_OK; -} - - -// static -nsresult nsBlinkTimer::RemoveBlinkFrame(nsIFrame* aFrame) -{ - NS_ASSERTION(sTextBlinker, "Should have blink timer here"); - - nsBlinkTimer* blinkTimer = sTextBlinker; // copy so we can call NS_RELEASE on it - if (!blinkTimer) return NS_OK; - - blinkTimer->RemoveFrame(aFrame); - NS_RELEASE(blinkTimer); - - return NS_OK; -} - -//---------------------------------------------------------------------- - -nsTextStyle::nsTextStyle(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* sc) -{ - // Get style data - mFont = sc->GetStyleFont(); - mText = sc->GetStyleText(); - - // Cache the original decorations and reuse the current font - // to query metrics, rather than creating a new font which is expensive. - nsFont* plainFont = (nsFont *)&mFont->mFont; //XXX: Change to use a CONST_CAST macro. - NS_ASSERTION(plainFont, "null plainFont: font problems in nsTextStyle::nsTextStyle"); - PRUint8 originalDecorations = plainFont->decorations; - plainFont->decorations = NS_FONT_DECORATION_NONE; - mAveCharWidth = 0; - // Set the font: some users of the struct expect this state - nsLayoutUtils::SetFontFromStyle(&aRenderingContext, sc); - aRenderingContext.GetFontMetrics(mNormalFont); - mNormalFont->GetSpaceWidth(mSpaceWidth); - mNormalFont->GetAveCharWidth(mAveCharWidth); - mLastFont = mNormalFont; - - // Get the small-caps font if needed - mSmallCaps = NS_STYLE_FONT_VARIANT_SMALL_CAPS == plainFont->variant; - if (mSmallCaps) { - nscoord originalSize = plainFont->size; - plainFont->size = nscoord(0.8 * plainFont->size); - mSmallFont = aPresContext->GetMetricsFor(*plainFont).get(); // addrefs - // Reset to the size value saved earlier. - plainFont->size = originalSize; - } - else { - mSmallFont = nsnull; - } - - // Reset to the decoration saved earlier - plainFont->decorations = originalDecorations; - - // Get the word and letter spacing - PRIntn unit = mText->mWordSpacing.GetUnit(); - if (eStyleUnit_Coord == unit) { - mWordSpacing = mText->mWordSpacing.GetCoordValue(); - } else { - mWordSpacing = 0; - } - - unit = mText->mLetterSpacing.GetUnit(); - if (eStyleUnit_Coord == unit) { - mLetterSpacing = mText->mLetterSpacing.GetCoordValue(); - } else { - mLetterSpacing = 0; - } - - mNumJustifiableCharacterToRender = 0; - mNumJustifiableCharacterToMeasure = 0; - mNumJustifiableCharacterReceivingExtraJot = 0; - mExtraSpacePerJustifiableCharacter = 0; - mPreformatted = (NS_STYLE_WHITESPACE_PRE == mText->mWhiteSpace) || - (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == mText->mWhiteSpace); - - mJustifying = (NS_STYLE_TEXT_ALIGN_JUSTIFY == mText->mTextAlign) && - !mPreformatted; -} - -nsTextStyle::~nsTextStyle() { - NS_IF_RELEASE(mNormalFont); - NS_IF_RELEASE(mSmallFont); -} - -//---------------------------------------------------------------------- - -inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB) -{ - if (colorA == colorB) { - nscolor res; - res = NS_RGB(NS_GET_R(colorA) ^ 0xff, - NS_GET_G(colorA) ^ 0xff, - NS_GET_B(colorA) ^ 0xff); - return res; - } - return colorA; -} - -//----------------------------------------------------------------------------- - -nsTextPaintStyle::nsTextPaintStyle(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsIContent* aContent, - PRInt16 aSelectionStatus) - : nsTextStyle(aPresContext, aRenderingContext, aStyleContext), - mPresContext(nsnull), - mStyleContext(nsnull), - mContent(nsnull), - mInitCommonColors(PR_FALSE), - mInitSelectionColors(PR_FALSE) -{ - mPresContext = aPresContext; - mStyleContext = aStyleContext; - mContent = aContent; - mSelectionStatus = aSelectionStatus; - mColor = mStyleContext->GetStyleColor(); - for (int i = 0; i < 4; i++) - mIMEColor[i].mInit = PR_FALSE; - mIMEUnderlineRelativeSize = -1.0f; -} - -nsTextPaintStyle::~nsTextPaintStyle() -{ - mColor = nsnull; -} - -PRBool -nsTextPaintStyle::EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor) -{ - - if (!aForeColor || !aBackColor) - return PR_FALSE; - - // If common colors are not initialized, mFrameBackgroundColor and - // mSufficientContrast are not initialized. - if (!mInitCommonColors && !InitCommonColors()) - return PR_FALSE; - - // If the combination of selection background color and frame background color - // is sufficient contrast, don't exchange the selection colors. - PRInt32 backLuminosityDifference = - NS_LUMINOSITY_DIFFERENCE(*aBackColor, mFrameBackgroundColor); - if (backLuminosityDifference >= mSufficientContrast) - return PR_FALSE; - - // Otherwise, we should use the higher-contrast color for the selection - // background color. - PRInt32 foreLuminosityDifference = - NS_LUMINOSITY_DIFFERENCE(*aForeColor, mFrameBackgroundColor); - if (backLuminosityDifference < foreLuminosityDifference) { - nscolor tmpColor = *aForeColor; - *aForeColor = *aBackColor; - *aBackColor = tmpColor; - return PR_TRUE; - } - return PR_FALSE; -} - -nscolor -nsTextPaintStyle::GetTextColor() -{ - return mColor->mColor; -} - -void -nsTextPaintStyle::GetSelectionColors(nscolor* aForeColor, - nscolor* aBackColor, - PRBool* aBackIsTransparent) -{ - NS_ASSERTION(aForeColor, "aForeColor is null"); - NS_ASSERTION(aBackColor, "aBackColor is null"); - NS_ASSERTION(aBackIsTransparent, "aBackIsTransparent is null"); - - if (!mInitSelectionColors && !InitSelectionColors()) { - NS_ERROR("Fail to initialize selection colors"); - return; - } - - *aForeColor = mSelectionTextColor; - *aBackColor = mSelectionBGColor; - *aBackIsTransparent = mSelectionBGIsTransparent; -} - -void -nsTextPaintStyle::GetIMESelectionColors(SelectionType aSelectionType, - nscolor* aForeColor, - nscolor* aBackColor, - PRBool* aBackIsTransparent) -{ - NS_ASSERTION(aForeColor, "aForeColor is null"); - NS_ASSERTION(aBackColor, "aBackColor is null"); - NS_ASSERTION(aBackIsTransparent, "aBackIsTransparent is null"); - - nsIMEColor* IMEColor = GetIMEColor(aSelectionType); - if (!IMEColor) { - NS_ERROR("aSelectionType is invalid"); - return; - } - if (!IMEColor->mInit) - return; - *aForeColor = IMEColor->mTextColor; - *aBackColor = IMEColor->mBGColor; - *aBackIsTransparent = IMEColor->mBGIsTransparent; -} - -PRBool -nsTextPaintStyle::GetIMEUnderline(SelectionType aSelectionType, - nscolor* aLineColor, - float* aRelativeSize) -{ - NS_ASSERTION(aLineColor, "aLineColor is null"); - NS_ASSERTION(aRelativeSize, "aRelativeSize is null"); - - nsIMEColor* IMEColor = GetIMEColor(aSelectionType); - if (!IMEColor) { - NS_ERROR("aSelectionType is invalid"); - return PR_FALSE; - } - if (!IMEColor->mInit) - return PR_FALSE; - if (IMEColor->mUnderlineColor == NS_TRANSPARENT || - mIMEUnderlineRelativeSize <= 0.0f) - return PR_FALSE; - - *aLineColor = IMEColor->mUnderlineColor; - *aRelativeSize = mIMEUnderlineRelativeSize; - return PR_TRUE; -} - -PRBool -nsTextPaintStyle::InitCommonColors() -{ - if (!mPresContext || !mStyleContext) - return PR_FALSE; - - if (mInitCommonColors) - return PR_TRUE; - - const nsStyleBackground* bg = - nsCSSRendering::FindNonTransparentBackground(mStyleContext); - NS_ASSERTION(bg, "Cannot find NonTransparentBackground."); - mFrameBackgroundColor = bg->mBackgroundColor; - - nsILookAndFeel* look = mPresContext->LookAndFeel(); - if (!look) - return PR_FALSE; - - nscolor defaultWindowBackgroundColor, selectionTextColor, selectionBGColor; - look->GetColor(nsILookAndFeel::eColor_TextSelectBackground, - selectionBGColor); - look->GetColor(nsILookAndFeel::eColor_TextSelectForeground, - selectionTextColor); - look->GetColor(nsILookAndFeel::eColor_WindowBackground, - defaultWindowBackgroundColor); - - mSufficientContrast = - PR_MIN(PR_MIN(NS_SUFFICIENT_LUMINOSITY_DIFFERENCE, - NS_LUMINOSITY_DIFFERENCE(selectionTextColor, - selectionBGColor)), - NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor, - selectionBGColor)); - - mInitCommonColors = PR_TRUE; - return PR_TRUE; -} - -PRBool -nsTextPaintStyle::InitSelectionColors() -{ - if (!mPresContext || !mStyleContext) - return PR_FALSE; - if (mInitSelectionColors) - return PR_TRUE; - - mSelectionBGIsTransparent = PR_FALSE; - - if (mContent && - mSelectionStatus == nsISelectionController::SELECTION_ON) { - nsRefPtr sc = nsnull; - sc = mPresContext->StyleSet()-> - ProbePseudoStyleFor(mContent->GetParent(), - nsCSSPseudoElements::mozSelection, mStyleContext); - // Use -moz-selection pseudo class. - if (sc) { - const nsStyleBackground* bg = sc->GetStyleBackground(); - mSelectionBGIsTransparent = - PRBool(bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT); - if (!mSelectionBGIsTransparent) - mSelectionBGColor = bg->mBackgroundColor; - mSelectionTextColor = sc->GetStyleColor()->mColor; - return PR_TRUE; - } - } - - nsILookAndFeel* look = mPresContext->LookAndFeel(); - if (!look) - return PR_FALSE; - - nscolor selectionBGColor; - look->GetColor(nsILookAndFeel::eColor_TextSelectBackground, - selectionBGColor); - - if (mSelectionStatus == nsISelectionController::SELECTION_ATTENTION) { - look->GetColor(nsILookAndFeel::eColor_TextSelectBackgroundAttention, - mSelectionBGColor); - mSelectionBGColor = EnsureDifferentColors(mSelectionBGColor, - selectionBGColor); - } else if (mSelectionStatus != nsISelectionController::SELECTION_ON) { - look->GetColor(nsILookAndFeel::eColor_TextSelectBackgroundDisabled, - mSelectionBGColor); - mSelectionBGColor = EnsureDifferentColors(mSelectionBGColor, - selectionBGColor); - } else { - mSelectionBGColor = selectionBGColor; - } - - look->GetColor(nsILookAndFeel::eColor_TextSelectForeground, - mSelectionTextColor); - - // On MacOS X, we don't exchange text color and BG color. - if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) { - mSelectionTextColor = EnsureDifferentColors(mColor->mColor, - mSelectionBGColor); - return PR_TRUE; - } - - EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor); - - mInitSelectionColors = PR_TRUE; - return PR_TRUE; -} - -nsTextPaintStyle::nsIMEColor* -nsTextPaintStyle::GetIMEColor(SelectionType aSelectionType) -{ - PRInt32 index; - switch (aSelectionType) { - case nsISelectionController::SELECTION_IME_RAWINPUT: - index = eIndexRawInput; - break; - case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: - index = eIndexSelRawText; - break; - case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: - index = eIndexConvText; - break; - case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: - index = eIndexSelConvText; - break; - default: - NS_ERROR("aSelectionType is Invalid"); - return nsnull; - } - nsIMEColor* IMEColor = &mIMEColor[index]; - if (!IMEColor->mInit && !InitIMEColors(aSelectionType, IMEColor)) - NS_ERROR("Fail to initialize IME color"); - return IMEColor; -} - -PRBool -nsTextPaintStyle::InitIMEColors(SelectionType aSelectionType, - nsIMEColor* aIMEColor) -{ - if (!mPresContext || !aIMEColor) - return PR_FALSE; - - NS_ASSERTION(!aIMEColor->mInit, "this is already initialized"); - - nsILookAndFeel::nsColorID foreColorID, backColorID, lineColorID; - switch (aSelectionType) { - case nsISelectionController::SELECTION_IME_RAWINPUT: - foreColorID = nsILookAndFeel::eColor_IMERawInputForeground; - backColorID = nsILookAndFeel::eColor_IMERawInputBackground; - lineColorID = nsILookAndFeel::eColor_IMERawInputUnderline; - break; - case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: - foreColorID = nsILookAndFeel::eColor_IMESelectedRawTextForeground; - backColorID = nsILookAndFeel::eColor_IMESelectedRawTextBackground; - lineColorID = nsILookAndFeel::eColor_IMESelectedRawTextUnderline; - break; - case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: - foreColorID = nsILookAndFeel::eColor_IMEConvertedTextForeground; - backColorID = nsILookAndFeel::eColor_IMEConvertedTextBackground; - lineColorID = nsILookAndFeel::eColor_IMEConvertedTextUnderline; - break; - case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: - foreColorID = nsILookAndFeel::eColor_IMESelectedConvertedTextForeground; - backColorID = nsILookAndFeel::eColor_IMESelectedConvertedTextBackground; - lineColorID = nsILookAndFeel::eColor_IMESelectedConvertedTextUnderline; - break; - default: - NS_ERROR("aSelectionType is Invalid"); - return PR_FALSE; - } - - nsILookAndFeel* look = mPresContext->LookAndFeel(); - if (!look) - return PR_FALSE; - - nscolor foreColor, backColor, lineColor; - look->GetColor(foreColorID, foreColor); - look->GetColor(backColorID, backColor); - look->GetColor(lineColorID, lineColor); - - // Convert special color to actual color - NS_ASSERTION(foreColor != NS_TRANSPARENT, - "foreColor cannot be NS_TRANSPARENT"); - NS_ASSERTION(backColor != NS_SAME_AS_FOREGROUND_COLOR, - "backColor cannot be NS_SAME_AS_FOREGROUND_COLOR"); - NS_ASSERTION(backColor != NS_40PERCENT_FOREGROUND_COLOR, - "backColor cannot be NS_40PERCENT_FOREGROUND_COLOR"); - - PRBool backIsTransparent = PR_FALSE; - if (backColor == NS_TRANSPARENT) - backIsTransparent = PR_TRUE; - - foreColor = GetResolvedForeColor(foreColor, GetTextColor(), backColor); - - if (!backIsTransparent) - EnsureSufficientContrast(&foreColor, &backColor); - - lineColor = GetResolvedForeColor(lineColor, foreColor, backColor); - - aIMEColor->mTextColor = foreColor; - aIMEColor->mBGColor = backColor; - aIMEColor->mBGIsTransparent = backIsTransparent; - aIMEColor->mUnderlineColor = lineColor; - aIMEColor->mInit = PR_TRUE; - - if (mIMEUnderlineRelativeSize == -1.0f) { - look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize, - mIMEUnderlineRelativeSize); - NS_ASSERTION(mIMEUnderlineRelativeSize >= 0.0f, - "underline size must be larger than 0"); - } - - return PR_TRUE; -} - -inline nscolor Get40PercentColor(nscolor aForeColor, nscolor aBackColor) -{ - nscolor foreColor = NS_RGBA(NS_GET_R(aForeColor), - NS_GET_G(aForeColor), - NS_GET_B(aForeColor), - (PRUint8)(255 * 0.4f)); - return NS_ComposeColors(aBackColor, foreColor); -} - -nscolor -nsTextPaintStyle::GetResolvedForeColor(nscolor aColor, - nscolor aDefaultForeColor, - nscolor aBackColor) -{ - if (aColor == NS_SAME_AS_FOREGROUND_COLOR) - return aDefaultForeColor; - - if (aColor != NS_40PERCENT_FOREGROUND_COLOR) - return aColor; - - // Get actual background color - nscolor actualBGColor = aBackColor; - if (actualBGColor == NS_TRANSPARENT) { - if (!mInitCommonColors && !InitCommonColors()) - return aDefaultForeColor; - actualBGColor = mFrameBackgroundColor; - } - return Get40PercentColor(aDefaultForeColor, actualBGColor); -} - -//----------------------------------------------------------------------------- - -#ifdef ACCESSIBILITY -NS_IMETHODIMP nsTextFrame::GetAccessible(nsIAccessible** aAccessible) -{ - if (!IsEmpty() || GetNextInFlow()) { - - nsCOMPtr accService = do_GetService("@mozilla.org/accessibilityService;1"); - - if (accService) { - return accService->CreateHTMLTextAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible); - } - } - return NS_ERROR_FAILURE; -} -#endif - - -//----------------------------------------------------------------------------- -NS_IMETHODIMP -nsTextFrame::Init(nsIContent* aContent, - nsIFrame* aParent, - nsIFrame* aPrevInFlow) -{ - NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT), - "Bogus content!"); - nsresult rv = nsFrame::Init(aContent, aParent, aPrevInFlow); - if (NS_SUCCEEDED(rv) && !aPrevInFlow && - GetStyleText()->WhiteSpaceIsSignificant()) { - // We care about our actual length in this case, so we can report the right - // thing from HasTerminalNewline(). Since we're not a continuing frame, we - // should map the whole content node. - - // Note that if we're created due to bidi splitting the bidi code - // will override what we compute here, so it's ok. - mContentLength = mContent->TextLength(); - } - return rv; -} - -void -nsTextFrame::Destroy() -{ - if (mNextContinuation) { - mNextContinuation->SetPrevInFlow(nsnull); - } - // Let the base class destroy the frame - nsFrame::Destroy(); -} - -class nsContinuingTextFrame : public nsTextFrame { -public: - friend nsIFrame* NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); - - NS_IMETHOD Init(nsIContent* aContent, - nsIFrame* aParent, - nsIFrame* aPrevInFlow); - - virtual void Destroy(); - - virtual nsIFrame* GetPrevContinuation() const { - return mPrevContinuation; - } - NS_IMETHOD SetPrevContinuation(nsIFrame* aPrevContinuation) { - NS_ASSERTION (!aPrevContinuation || GetType() == aPrevContinuation->GetType(), - "setting a prev continuation with incorrect type!"); - NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevContinuation, this), - "creating a loop in continuation chain!"); - mPrevContinuation = aPrevContinuation; - RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION); - return NS_OK; - } - virtual nsIFrame* GetPrevInFlowVirtual() const { return GetPrevInFlow(); } - nsIFrame* GetPrevInFlow() const { - return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nsnull; - } - NS_IMETHOD SetPrevInFlow(nsIFrame* aPrevInFlow) { - NS_ASSERTION (!aPrevInFlow || GetType() == aPrevInFlow->GetType(), - "setting a prev in flow with incorrect type!"); - NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevInFlow, this), - "creating a loop in continuation chain!"); - mPrevContinuation = aPrevInFlow; - AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION); - return NS_OK; - } - virtual nsIFrame* GetFirstInFlow() const; - virtual nsIFrame* GetFirstContinuation() const; - - virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext, - InlineMinWidthData *aData); - virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext, - InlinePrefWidthData *aData); - -protected: - nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {} - nsIFrame* mPrevContinuation; -}; - -NS_IMETHODIMP -nsContinuingTextFrame::Init(nsIContent* aContent, - nsIFrame* aParent, - nsIFrame* aPrevInFlow) -{ - nsresult rv = nsTextFrame::Init(aContent, aParent, aPrevInFlow); - - if (aPrevInFlow) { - nsIFrame* nextContinuation = aPrevInFlow->GetNextContinuation(); - // Hook the frame into the flow - SetPrevInFlow(aPrevInFlow); - aPrevInFlow->SetNextInFlow(this); -#ifdef IBMBIDI - if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) { - PRInt32 start, end; - aPrevInFlow->GetOffsets(start, mContentOffset); - - nsPropertyTable *propTable = PresContext()->PropertyTable(); - propTable->SetProperty(this, nsGkAtoms::embeddingLevel, - propTable->GetProperty(aPrevInFlow, nsGkAtoms::embeddingLevel), - nsnull, nsnull); - propTable->SetProperty(this, nsGkAtoms::baseLevel, - propTable->GetProperty(aPrevInFlow, nsGkAtoms::baseLevel), - nsnull, nsnull); - propTable->SetProperty(this, nsGkAtoms::charType, - propTable->GetProperty(aPrevInFlow, nsGkAtoms::charType), - nsnull, nsnull); - if (nextContinuation) { - SetNextContinuation(nextContinuation); - nextContinuation->SetPrevContinuation(this); - nextContinuation->GetOffsets(start, end); - mContentLength = PR_MAX(1, start - mContentOffset); - } - mState |= NS_FRAME_IS_BIDI; - } // prev frame is bidi -#endif // IBMBIDI - } - - return rv; -} - -void -nsContinuingTextFrame::Destroy() -{ - if (mPrevContinuation || mNextContinuation) { - nsSplittableFrame::RemoveFromFlow(this); - } - // Let the base class destroy the frame - nsFrame::Destroy(); -} - -nsIFrame* -nsContinuingTextFrame::GetFirstInFlow() const -{ - // Can't cast to |nsContinuingTextFrame*| because the first one isn't. - nsIFrame *firstInFlow, - *previous = NS_CONST_CAST(nsIFrame*, - NS_STATIC_CAST(const nsIFrame*, this)); - do { - firstInFlow = previous; - previous = firstInFlow->GetPrevInFlow(); - } while (previous); - return firstInFlow; -} - -nsIFrame* -nsContinuingTextFrame::GetFirstContinuation() const -{ - // Can't cast to |nsContinuingTextFrame*| because the first one isn't. - nsIFrame *firstContinuation, - *previous = NS_CONST_CAST(nsIFrame*, - NS_STATIC_CAST(const nsIFrame*, mPrevContinuation)); - do { - firstContinuation = previous; - previous = firstContinuation->GetPrevContinuation(); - } while (previous); - return firstContinuation; -} - -// XXX Do we want to do all the work for the first-in-flow or do the -// work for each part? (Be careful of first-letter / first-line, though, -// especially first-line!) Doing all the work on the first-in-flow has -// the advantage of avoiding the potential for incremental reflow bugs, -// but depends on our maintining the frame tree in reasonable ways even -// for edge cases (block-within-inline splits, nextBidi, etc.) - -// XXX We really need to make :first-letter happen during frame -// construction. - -// Needed for text frames in XUL. -/* virtual */ nscoord -nsTextFrame::GetMinWidth(nsIRenderingContext *aRenderingContext) -{ - return nsLayoutUtils::MinWidthFromInline(this, aRenderingContext); -} - -// Needed for text frames in XUL. -/* virtual */ nscoord -nsTextFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext) -{ - return nsLayoutUtils::PrefWidthFromInline(this, aRenderingContext); -} - -/* virtual */ void -nsContinuingTextFrame::AddInlineMinWidth(nsIRenderingContext *aRenderingContext, - InlineMinWidthData *aData) -{ - // Do nothing, since the first-in-flow accounts for everything. - return; -} - -/* virtual */ void -nsContinuingTextFrame::AddInlinePrefWidth(nsIRenderingContext *aRenderingContext, - InlinePrefWidthData *aData) -{ - // Do nothing, since the first-in-flow accounts for everything. - return; -} - -//DRAW SELECTION ITERATOR USED FOR TEXTFRAMES ONLY -//helper class for drawing multiply selected text -class DrawSelectionIterator -{ -public: - DrawSelectionIterator(const SelectionDetails *aSelDetails, PRUnichar *aText, - PRUint32 aTextLength, nsTextPaintStyle *aTextStyle, - SelectionType aCareSelections); - ~DrawSelectionIterator(); - PRBool First(); - PRBool Next(); - PRBool IsDone(); - PRBool IsLast(); - - PRUnichar * CurrentTextUnicharPtr(); - char * CurrentTextCStrPtr(); - PRUint32 CurrentLength(); - PRBool IsBeforeOrAfter(); - - /** - * Get foreground color, background color, whether the background is transparent, - * and whether the current range is the normal selection. - * - * @param aForeColor [out] returns the foreground color of the current range. - * @param aBackColor [out] returns the background color of the current range. - * Note that this value is undefined if aBackIsTransparent - * is true or if @return is false. - * @param aBackIsTransparent [out] returns whether the background is transparent. - * If true, the background is transparent. - * Otherwise, it isn't so. - * @return whether the current range is a normal selection. - */ - PRBool GetSelectionColors(nscolor *aForeColor, nscolor *aBackColor, PRBool *aBackIsTransparent); -private: - union { - PRUnichar *mUniStr; - char *mCStr; - }; - PRUint32 mLength; - PRUint32 mCurrentIdx; - PRUint32 mCurrentLength; - nsTextPaintStyle* mOldStyle;//base new styles on this one??? - const SelectionDetails *mDetails; - PRBool mDone; - PRUint8 * mTypes; - PRBool mInit; - //private methods - void FillCurrentData(); -}; - -DrawSelectionIterator::DrawSelectionIterator(const SelectionDetails *aSelDetails, - PRUnichar *aText, - PRUint32 aTextLength, - nsTextPaintStyle* aTextStyle, - SelectionType aCareSelections) - :mOldStyle(aTextStyle) -{ - NS_ASSERTION(aCareSelections, "aCareSelection value must not be zero!"); - - mDetails = aSelDetails; - mCurrentIdx = 0; - mUniStr = aText; - mLength = aTextLength; - mTypes = nsnull; - mInit = PR_FALSE; - - if (!aSelDetails) { - mDone = PR_TRUE; - return; - } - mDone = (PRBool)(mCurrentIdx>=mLength); - if (mDone) - return; - - //special case for 1 selection. later - const SelectionDetails *details = aSelDetails; - if (details->mNext) { - // go to next - } else if (details->mStart == details->mEnd) { - // no collapsed selections here! - mDone = PR_TRUE; - return; - } else if (!(details->mType & aCareSelections)) { - //if all we have is selection we DONT care about, do nothing - mDone = PR_TRUE; - return; - } - - mTypes = new PRUint8[mLength]; - if (!mTypes) - return; - memset(mTypes, 0, mLength); - while (details) { - if ((details->mType & aCareSelections) && - (details->mStart != details->mEnd)) { - mInit = PR_TRUE; // WE FOUND SOMETHING WE CARE ABOUT - for (int i = details->mStart; i < details->mEnd; i++) { - if ((PRUint32)i >= mLength) { - NS_ASSERTION(0, "Selection Details out of range?"); - return; - } - mTypes[i] |= details->mType; - } - } - details= details->mNext; - } - if (!mInit) { - // we have details but none that we care about. - delete [] mTypes; - mTypes = nsnull; - mDone = PR_TRUE; // we are finished - mInit = PR_TRUE; - } -} - -DrawSelectionIterator::~DrawSelectionIterator() -{ - if (mTypes) - delete [] mTypes; -} - -void -DrawSelectionIterator::FillCurrentData() -{ - if (mDone) - return; - mCurrentIdx += mCurrentLength; // advance to this chunk - mCurrentLength = 0; - if (mCurrentIdx >= mLength) - { - mDone = PR_TRUE; - return; - } - if (!mTypes) - { - if (mCurrentIdx < (PRUint32)mDetails->mStart) - { - mCurrentLength = mDetails->mStart; - } - else if (mCurrentIdx == (PRUint32)mDetails->mStart) - {//start - mCurrentLength = mDetails->mEnd-mCurrentIdx; - } - else if (mCurrentIdx > (PRUint32)mDetails->mStart)//last unselected part - { - mCurrentLength = mLength - mDetails->mEnd; - } - } - else - { - uint8 typevalue = mTypes[mCurrentIdx]; - while (mCurrentIdx+mCurrentLength < mLength && typevalue == mTypes[mCurrentIdx+mCurrentLength]) - { - mCurrentLength++; - } - } - // never overrun past mLength - if (mCurrentIdx+mCurrentLength > mLength) - { - mCurrentLength = mLength - mCurrentIdx; - } -} - -PRBool -DrawSelectionIterator::First() -{ - if (!mInit) - return PR_FALSE; - mCurrentIdx = 0; - mCurrentLength = 0; - if (!mTypes && mDetails->mStart == mDetails->mEnd)//no collapsed selections here! - mDone = PR_TRUE; - mDone = (mCurrentIdx+mCurrentLength) >= mLength; - FillCurrentData(); - return PR_TRUE; -} - - - -PRBool -DrawSelectionIterator::Next() -{ - if (mDone || !mInit) - return PR_FALSE; - FillCurrentData();//advances to next chunk - return PR_TRUE; -} - -PRBool -DrawSelectionIterator::IsLast() -{ - return mDone || !mInit || mCurrentIdx + mCurrentLength >= mLength; -} - -PRBool -DrawSelectionIterator::IsDone() -{ - return mDone || !mInit; -} - - -PRUnichar * -DrawSelectionIterator::CurrentTextUnicharPtr() -{ - return mUniStr+mCurrentIdx; -} - -char * -DrawSelectionIterator::CurrentTextCStrPtr() -{ - return mCStr+mCurrentIdx; -} - -PRUint32 -DrawSelectionIterator::CurrentLength() -{ - return mCurrentLength; -} - -PRBool -DrawSelectionIterator::GetSelectionColors(nscolor *aForeColor, - nscolor *aBackColor, - PRBool *aBackIsTransparent) -{ - if (mTypes) { - // Normal selection - if (mTypes[mCurrentIdx] & nsTextPaintStyle::eNormalSelection) { - mOldStyle->GetSelectionColors(aForeColor, aBackColor, - aBackIsTransparent); - return PR_TRUE; - } - - // IME selections - if (mTypes[mCurrentIdx] & nsTextPaintStyle::eIMESelections) { - mOldStyle->GetIMESelectionColors(mTypes[mCurrentIdx], - aForeColor, aBackColor, - aBackIsTransparent); - return PR_TRUE; - } - } - - // Non-supported Selection or Non-selection text - *aBackIsTransparent = PR_FALSE; - *aForeColor = mOldStyle->GetTextColor(); - return PR_FALSE; -} - -PRBool -DrawSelectionIterator::IsBeforeOrAfter() -{ - return mCurrentIdx != (PRUint32)mDetails->mStart; -} - -//END DRAWSELECTIONITERATOR!! - - - - -// Flag information used by rendering code. This information is -// computed by the ResizeReflow code. The flags are stored in the -// mState variable in the frame class private section. - -// Flag indicating that whitespace was skipped -#define TEXT_SKIP_LEADING_WS 0x01000000 -#define TEXT_HAS_MULTIBYTE 0x02000000 -#define TEXT_IN_WORD 0x04000000 -// This bit is set on the first frame in a continuation indicating -// that it was chopped short because of :first-letter style. -#define TEXT_FIRST_LETTER 0x08000000 -#define TEXT_WAS_TRANSFORMED 0x10000000 - -// Bits in mState used for reflow flags -#define TEXT_REFLOW_FLAGS 0x1F000000 - -#define TEXT_TRIMMED_WS 0x20000000 - -#define TEXT_OPTIMIZE_RESIZE 0x40000000 - -#define TEXT_BLINK_ON 0x80000000 - -#define TEXT_IS_ONLY_WHITESPACE 0x00100000 - -#define TEXT_ISNOT_ONLY_WHITESPACE 0x00200000 - -#define TEXT_WHITESPACE_FLAGS 0x00300000 - -#define TEXT_IS_END_OF_LINE 0x00400000 - -//---------------------------------------------------------------------- - -#if defined(DEBUG_rbs) || defined(DEBUG_bzbarsky) -static void -VerifyNotDirty(nsFrameState state) -{ - PRBool isZero = state & NS_FRAME_FIRST_REFLOW; - PRBool isDirty = state & NS_FRAME_IS_DIRTY; - if (!isZero && isDirty) - NS_WARNING("internal offsets may be out-of-sync"); -} -#define DEBUG_VERIFY_NOT_DIRTY(state) \ -VerifyNotDirty(state) -#else -#define DEBUG_VERIFY_NOT_DIRTY(state) -#endif - -nsIFrame* -NS_NewTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) -{ - return new (aPresShell) nsTextFrame(aContext); -} - -nsIFrame* -NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) -{ - return new (aPresShell) nsContinuingTextFrame(aContext); -} - -nsTextFrame::~nsTextFrame() -{ - if (0 != (mState & TEXT_BLINK_ON)) - { - nsBlinkTimer::RemoveBlinkFrame(this); - } -} - -nsIDocument* -nsTextFrame::GetDocument(nsPresContext* aPresContext) -{ - nsIDocument *result = nsnull; - if (mContent) { - result = mContent->GetDocument(); - } - if (!result && aPresContext) { - result = aPresContext->PresShell()->GetDocument(); - } - return result; -} - -NS_IMETHODIMP -nsTextFrame::GetCursor(const nsPoint& aPoint, - nsIFrame::Cursor& aCursor) -{ - FillCursorInformationFromStyle(GetStyleUserInterface(), aCursor); - if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) { - aCursor.mCursor = NS_STYLE_CURSOR_TEXT; - - // If tabindex >= 0, use default cursor to indicate it's not selectable - nsIFrame *ancestorFrame = this; - while ((ancestorFrame = ancestorFrame->GetParent()) != nsnull) { - nsIContent *ancestorContent = ancestorFrame->GetContent(); - if (ancestorContent && ancestorContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) { - nsAutoString tabIndexStr; - ancestorContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr); - if (!tabIndexStr.IsEmpty()) { - PRInt32 rv, tabIndexVal = tabIndexStr.ToInteger(&rv); - if (NS_SUCCEEDED(rv) && tabIndexVal >= 0) { - aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT; - break; - } - } - } - } - } - - return NS_OK; -} - -nsIFrame* -nsTextFrame::GetLastInFlow() const -{ - nsTextFrame* lastInFlow = NS_CONST_CAST(nsTextFrame*, this); - while (lastInFlow->GetNextInFlow()) { - lastInFlow = NS_STATIC_CAST(nsTextFrame*, lastInFlow->GetNextInFlow()); - } - NS_POSTCONDITION(lastInFlow, "illegal state in flow chain."); - return lastInFlow; -} -nsIFrame* -nsTextFrame::GetLastContinuation() const -{ - nsTextFrame* lastInFlow = NS_CONST_CAST(nsTextFrame*, this); - while (lastInFlow->mNextContinuation) { - lastInFlow = NS_STATIC_CAST(nsTextFrame*, lastInFlow->mNextContinuation); - } - NS_POSTCONDITION(lastInFlow, "illegal state in continuation chain."); - return lastInFlow; -} - - -NS_IMETHODIMP -nsTextFrame::CharacterDataChanged(nsPresContext* aPresContext, - nsIContent* aChild, - PRBool aAppend) -{ - nsIFrame* targetTextFrame = this; - - if (aAppend) { - nsTextFrame* frame = NS_STATIC_CAST(nsTextFrame*, GetLastContinuation()); - frame->mState &= ~TEXT_WHITESPACE_FLAGS; - targetTextFrame = frame; - } else { - // Mark this frame and all the next-in-flow frames as dirty and reset all - // the content offsets and lengths to 0, since they no longer know what - // content is ok to access. - - // Don't set NS_FRAME_IS_DIRTY on |this|, since we call FrameNeedsReflow - // below. - nsTextFrame* textFrame = this; - do { - textFrame->mState &= ~TEXT_WHITESPACE_FLAGS; - textFrame->mContentOffset = 0; - textFrame->mContentLength = 0; - textFrame = NS_STATIC_CAST(nsTextFrame*, textFrame->GetNextContinuation()); - if (!textFrame) { - break; - } - textFrame->mState |= NS_FRAME_IS_DIRTY; - } while (1); - } - - // Ask the parent frame to reflow me. - aPresContext->GetPresShell()->FrameNeedsReflow(targetTextFrame, - nsIPresShell::eStyleChange, - NS_FRAME_IS_DIRTY); - - return NS_OK; -} - -// When we fix nsTextFrame to handle bearing (character glyphs that -// extend outside the frame) by giving it overflow area, we'll need to fix -// this to use the overflow area as its bounds. -class nsDisplayText : public nsDisplayItem { -public: - nsDisplayText(nsTextFrame* aFrame) : nsDisplayItem(aFrame) { - MOZ_COUNT_CTOR(nsDisplayText); - } -#ifdef NS_BUILD_REFCNT_LOGGING - virtual ~nsDisplayText() { - MOZ_COUNT_DTOR(nsDisplayText); - } -#endif - - virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt) { return mFrame; } - virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx, - const nsRect& aDirtyRect); - NS_DISPLAY_DECL_NAME("Text") -}; - -void -nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, - nsIRenderingContext* aCtx, const nsRect& aDirtyRect) { - NS_STATIC_CAST(nsTextFrame*, mFrame)-> - PaintText(*aCtx, aBuilder->ToReferenceFrame(mFrame)); -} - -NS_IMETHODIMP -nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, - const nsRect& aDirtyRect, - const nsDisplayListSet& aLists) -{ - if (!IsVisibleForPainting(aBuilder)) - return NS_OK; - - DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame"); - - if ((0 != (mState & TEXT_BLINK_ON)) && nsBlinkTimer::GetBlinkIsOff()) - return NS_OK; - - return aLists.Content()->AppendNewToTop(new (aBuilder) nsDisplayText(this)); -} - -void -nsTextFrame::PaintText(nsIRenderingContext& aRenderingContext, nsPoint aPt) -{ - nsStyleContext* sc = mStyleContext; - nsPresContext* presContext = PresContext(); - nsCOMPtr content; - PRInt32 offset, length; - GetContentAndOffsetsForSelection(presContext, - getter_AddRefs(content), - &offset, &length); - PRInt16 selectionValue; - if (NS_FAILED(GetSelectionStatus(presContext, selectionValue))) - selectionValue = nsISelectionController::SELECTION_NORMAL; - - nsTextPaintStyle ts(presContext, aRenderingContext, mStyleContext, content, - selectionValue); - SetupTextRunDirection(presContext, &aRenderingContext); - if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing) - || ts.mJustifying) { - PaintTextSlowly(presContext, aRenderingContext, sc, ts, aPt.x, aPt.y); - } - else { - // Get the text fragment - const nsTextFragment* frag = mContent->GetText(); - if (!frag) { - return; - } - - // Choose rendering pathway based on rendering context performance - // hint, whether it needs to be transformed, and whether it's - // multi-byte - PRBool hasMultiByteChars = (0 != (mState & TEXT_HAS_MULTIBYTE)); - PRUint32 hints = 0; - aRenderingContext.GetHints(hints); - -#ifdef IBMBIDI - PRBool bidiEnabled = presContext->BidiEnabled(); -#else - const PRBool bidiEnabled = PR_FALSE; -#endif // IBMBIDI - // * BiDi text or text with multi-byte characters must always be - // rendered as Unicode. - // * Non-transformed, 1-byte text should always be rendered as - // ASCII. - // * Other transformed or 2-byte text should be rendered according - // to the preference of the hint from the rendering context. - if (bidiEnabled || hasMultiByteChars || - ((0 == (hints & NS_RENDERING_HINT_FAST_8BIT_TEXT)) && - (frag->Is2b() || (0 != (mState & TEXT_WAS_TRANSFORMED))))) { - PaintUnicodeText(presContext, aRenderingContext, sc, ts, aPt.x, aPt.y); - } - else { - PaintAsciiText(presContext, aRenderingContext, sc, ts, aPt.x, aPt.y); - } - } -} - -PRBool -nsTextFrame::IsChineseJapaneseLangGroup() -{ - const nsStyleVisibility* visibility = mStyleContext->GetStyleVisibility(); - if (visibility->mLangGroup == nsGkAtoms::Japanese - || visibility->mLangGroup == nsGkAtoms::Chinese - || visibility->mLangGroup == nsGkAtoms::Taiwanese - || visibility->mLangGroup == nsGkAtoms::HongKongChinese) - return PR_TRUE; - - return PR_FALSE; -} - -/* - * Currently only Unicode characters below 0x10000 have their spacing modified - * by justification. If characters above 0x10000 turn out to need - * justification spacing, that will require extra work. Currently, - * this function must not include 0xd800 to 0xdbff because these characters - * are surrogates. - */ -PRBool -nsTextFrame::IsJustifiableCharacter(PRUnichar aChar, PRBool aLangIsCJ) -{ - if (0x20u == aChar || 0xa0u == aChar) - return PR_TRUE; - if (aChar < 0x2150u) - return PR_FALSE; - if (aLangIsCJ && ( - (0x2150u <= aChar && aChar <= 0x22ffu) || // Number Forms, Arrows, Mathematical Operators - (0x2460u <= aChar && aChar <= 0x24ffu) || // Enclosed Alphanumerics - (0x2580u <= aChar && aChar <= 0x27bfu) || // Block Elements, Geometric Shapes, Miscellaneous Symbols, Dingbats - (0x27f0u <= aChar && aChar <= 0x2bffu) || // Supplemental Arrows-A, Braille Patterns, Supplemental Arrows-B, - // Miscellaneous Mathematical Symbols-B, Supplemental Mathematical Operators, - // Miscellaneous Symbols and Arrows - (0x2e80u <= aChar && aChar <= 0x312fu) || // CJK Radicals Supplement, CJK Radicals Supplement, - // Ideographic Description Characters, CJK Symbols and Punctuation, - // Hiragana, Katakana, Bopomofo - (0x3190u <= aChar && aChar <= 0xabffu) || // Kanbun, Bopomofo Extended, Katakana Phonetic Extensions, - // Enclosed CJK Letters and Months, CJK Compatibility, - // CJK Unified Ideographs Extension A, Yijing Hexagram Symbols, - // CJK Unified Ideographs, Yi Syllables, Yi Radicals - (0xf900u <= aChar && aChar <= 0xfaffu) || // CJK Compatibility Ideographs - (0xff5eu <= aChar && aChar <= 0xff9fu) // Halfwidth and Fullwidth Forms(a part) - )) - return PR_TRUE; - return PR_FALSE; -} - -nsresult -nsTextFrame::FillClusterBuffer(nsPresContext *aPresContext, const PRUnichar *aText, - PRUint32 aLength, nsAutoPRUint8Buffer& aClusterBuffer) -{ - nsresult rv = aClusterBuffer.GrowTo(aLength); - NS_ENSURE_SUCCESS(rv, rv); - - // Fill in the cluster hint information, if it's available. - nsCOMPtr acx; - PRUint32 clusterHint = 0; - - nsIPresShell *shell = aPresContext->GetPresShell(); - if (shell) { - rv = shell->CreateRenderingContext(this, getter_AddRefs(acx)); - NS_ENSURE_SUCCESS(rv, rv); - - // Find the font metrics for this text - nsLayoutUtils::SetFontFromStyle(acx, mStyleContext); - - acx->GetHints(clusterHint); - clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS; - } - - if (clusterHint) { - rv = acx->GetClusterInfo(aText, aLength, aClusterBuffer.mBuffer); - } - else { - memset(aClusterBuffer.mBuffer, 1, sizeof(PRInt8) * aLength); - } - - return rv; -} - -inline PRBool IsEndOfLine(nsFrameState aState) -{ - return (aState & TEXT_IS_END_OF_LINE) ? PR_TRUE : PR_FALSE; -} - -void nsTextFrame::SetupTextRunDirection(nsPresContext* aPresContext, - nsIRenderingContext* aContext) -{ - PRBool isRTL = aPresContext->BidiEnabled() && (NS_GET_EMBEDDING_LEVEL(this) & 1); - aContext->SetTextRunRTL(isRTL); -} - -/** - * Prepare the text in the content for rendering. If aIndexes is not nsnull - * then fill in aIndexes's with the mapping from the original input to - * the prepared output. - */ -void -nsTextFrame::PrepareUnicodeText(nsTextTransformer& aTX, - nsAutoIndexBuffer* aIndexBuffer, - nsAutoTextBuffer* aTextBuffer, - PRInt32* aTextLen, - PRBool aForceArabicShaping, - PRIntn* aJustifiableCharCount, - PRBool aRemoveMultipleTrimmedWS) -{ - // Setup transform to operate starting in the content at our content - // offset - aTX.Init(this, mContent, mContentOffset, aForceArabicShaping); - - PRInt32 strInx = mContentOffset; - PRInt32* indexp = aIndexBuffer ? aIndexBuffer->mBuffer : nsnull; - - // Skip over the leading whitespace - PRInt32 n = mContentLength; - if (0 != (mState & TEXT_SKIP_LEADING_WS)) { - PRBool isWhitespace, wasTransformed; - PRInt32 wordLen, contentLen; - // Set maximum word length. This is an ABUSE of the variable - // because on entry, this is DOM content length, but on exit, - // GetNextWord returns a transformed-string length in here! - wordLen = mContentOffset + mContentLength; - aTX.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed); - // we trip this assertion in bug 31053, but I think it's unnecessary - //NS_ASSERTION(isWhitespace, "mState and content are out of sync"); - - if (isWhitespace) { - if (nsnull != indexp) { - // Point mapping indicies at the same content index since - // all of the compressed whitespace maps down to the same - // renderable character. - PRInt32 i = contentLen; - while (--i >= 0) { - *indexp++ = strInx; - } - } - n -= contentLen; - if(n<0) - NS_WARNING("mContentLength is < FragmentLength"); - } - } - - // Rescan the content and transform it. Stop when we have consumed - // mContentLength characters. - PRUint8 textTransform = GetStyleText()->mTextTransform; - PRBool inWord = (TEXT_IN_WORD & mState) ? PR_TRUE : PR_FALSE; - PRInt32 column = mColumn; - PRInt32 textLength = 0; - PRInt32 dstOffset = 0; - - nsAutoTextBuffer tmpTextBuffer; - nsAutoTextBuffer* textBuffer = aTextBuffer; - if (!textBuffer && aJustifiableCharCount) - textBuffer = &tmpTextBuffer; - - while (n > 0) { - PRUnichar* bp; - PRBool isWhitespace, wasTransformed; - PRInt32 wordLen, contentLen; - - // Set maximum word length. This is an ABUSE of the variable - // because on entry, this is DOM content length, but on exit, - // GetNextWord returns a transformed-string length in here! - wordLen = mContentOffset + mContentLength; - // Get the next word - bp = aTX.GetNextWord(inWord, &wordLen, &contentLen, &isWhitespace, &wasTransformed); - if (nsnull == bp) { - if (indexp) { - while (--n >= 0) { - *indexp++ = strInx; - } - } - break; - } - inWord = PR_FALSE; - if (isWhitespace) { - if ('\t' == bp[0]) { - PRInt32 spaces = 8 - (7 & column); - PRUnichar* tp = bp; - wordLen = spaces; - while (--spaces >= 0) { - *tp++ = ' '; - } - // XXX This is a one to many mapping that I think isn't handled well - if (nsnull != indexp) { - *indexp++ = strInx; - strInx += wordLen; - } - } - else if ('\n' == bp[0]) { - if (nsnull != indexp) { - *indexp++ = strInx; - } - break; - } - else if (nsnull != indexp) { - if (1 == wordLen) { - // Point mapping indicies at the same content index since - // all of the compressed whitespace maps down to the same - // renderable character. - PRInt32 i = contentLen; - while (--i >= 0) { - *indexp++ = strInx; - } - strInx++; - } else { - // Point mapping indicies at each content index in the word - PRInt32 i = contentLen; - while (--i >= 0) { - *indexp++ = strInx++; - } - } - } - } - else { - PRInt32 i = contentLen; - if (nsnull != indexp) { - // Point mapping indices at each content index in the word - if (!wasTransformed) { - while (--i >= 0) { - *indexp++ = strInx++; - } - } else { - PRUnichar* tp = bp; - PRBool caseChanged = - textTransform == NS_STYLE_TEXT_TRANSFORM_UPPERCASE || - textTransform == NS_STYLE_TEXT_TRANSFORM_CAPITALIZE; - while (--i >= 0) { - PRUnichar ch = aTX.GetContentCharAt(mContentOffset + - indexp - aIndexBuffer->mBuffer); - if (IS_DISCARDED(ch) || ch == '\n') { - *indexp++ = strInx; - continue; - } - // Point lam and alef to lamalef in shaped text - if (aTX.NeedsArabicShaping()) { - if (IS_LAM(ch) && IS_LAMALEF(*tp)) { - // No need to check for index > length, since - // GetContentCharAt() checks - PRUnichar ch1 = aTX.GetContentCharAt(mContentOffset + - indexp + 1 - aIndexBuffer->mBuffer); - if (IS_ALEF(ch1)) { - *indexp++ = strInx; - --i; - } - } - } - *indexp++ = strInx++; - // Point any capitalized German ß to 'SS' - if (caseChanged && ch == kSZLIG && *tp == PRUnichar('S')) { - ++strInx; - ++tp; - } - ++tp; - } - } - } - } - - // Grow the buffer before we run out of room. - if (textBuffer != nsnull && dstOffset + wordLen > textBuffer->mBufferLen) { - nsresult rv = textBuffer->GrowBy(wordLen); - if (NS_FAILED(rv)) { - break; - } - } - - column += wordLen; - textLength += wordLen; - n -= contentLen; - if (textBuffer != nsnull) { - memcpy(textBuffer->mBuffer + dstOffset, bp, - sizeof(PRUnichar)*wordLen); - } - dstOffset += wordLen; - } - -#ifdef DEBUG - if (aIndexBuffer) { - NS_ASSERTION(indexp <= aIndexBuffer->mBuffer + aIndexBuffer->mBufferLen, - "yikes - we just overwrote memory"); - } - if (textBuffer) { - NS_ASSERTION(dstOffset <= textBuffer->mBufferLen, - "yikes - we just overwrote memory"); - } - -#endif - - // Remove trailing whitespace if it was trimmed after reflow - // TEXT_TRIMMED_WS can be set in measureText during reflow, and - // nonexitent text buffer may occur in this situation. - if (TEXT_TRIMMED_WS & mState && textBuffer) { - while (--dstOffset >= 0) { - PRUnichar ch = textBuffer->mBuffer[dstOffset]; - if (XP_IS_SPACE(ch)) - textLength--; - else - break; - if (!aRemoveMultipleTrimmedWS) - break; - } - } - - if (aIndexBuffer) { - PRInt32* ip = aIndexBuffer->mBuffer; - // Make sure no indexes point beyond text length - for (PRInt32 i = mContentLength - 1; i >= 0; i--) { - if (ip[i] > textLength + mContentOffset) - ip[i] = textLength + mContentOffset; - else - break; - } - ip[mContentLength] = ip[mContentLength-1]; - if ((ip[mContentLength] - mContentOffset) < textLength) { - // Must set up last one for selection beyond edge if in boundary - ip[mContentLength] = textLength + mContentOffset; - } - } - - *aTextLen = textLength; - - if (aJustifiableCharCount && textBuffer) { - PRBool isCJ = IsChineseJapaneseLangGroup(); - PRIntn numJustifiableCharacter = 0; - PRInt32 justifiableRange = textLength; - if (IsEndOfLine(mState)) - justifiableRange--; - for (PRInt32 i = 0; i < justifiableRange; i++) { - if (IsJustifiableCharacter(textBuffer->mBuffer[i], isCJ)) - numJustifiableCharacter++; - } - *aJustifiableCharCount = numJustifiableCharacter; - } -} - - -//#define SHOW_SELECTION_CURSOR // should be turned off when the caret code is activated - -#ifdef SHOW_SELECTION_CURSOR - -// XXX This clearly needs to be done by the container, *somehow* -#define CURSOR_COLOR NS_RGB(0,0,255) -static void -RenderSelectionCursor(nsIRenderingContext& aRenderingContext, - nscoord dx, nscoord dy, nscoord aHeight, - nscolor aCursorColor) -{ - nsPoint pnts[4]; - nscoord ox = aHeight / 4; - nscoord oy = ox; - nscoord x0 = dx; - nscoord y0 = dy + aHeight; - pnts[0].x = x0 - ox; - pnts[0].y = y0; - pnts[1].x = x0; - pnts[1].y = y0 - oy; - pnts[2].x = x0 + ox; - pnts[2].y = y0; - pnts[3].x = x0 - ox; - pnts[3].y = y0; - - // Draw little blue triangle - aRenderingContext.SetColor(aCursorColor); - aRenderingContext.FillPolygon(pnts, 4); -} - -#endif - -void -nsTextFrame::PaintTextDecorations(nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsPresContext* aPresContext, - nsTextPaintStyle& aTextStyle, - nscoord aX, nscoord aY, nscoord aWidth, - PRBool aRightToLeftText, - PRUnichar *aText, /*=nsnull*/ - SelectionDetails *aDetails,/*= nsnull*/ - PRUint32 aIndex, /*= 0*/ - PRUint32 aLength, /*= 0*/ - const nscoord* aSpacing /* = nsnull*/ ) - -{ - // Quirks mode text decoration are rendered by children; see bug 1777 - // In non-quirks mode, nsHTMLContainer::Paint and nsBlockFrame::Paint - // does the painting of text decorations. - if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { - nscolor overColor, underColor, strikeColor; - - PRBool useOverride = PR_FALSE; - nscolor overrideColor; - - PRUint8 decorations = NS_STYLE_TEXT_DECORATION_NONE; - // A mask of all possible decorations. - PRUint8 decorMask = NS_STYLE_TEXT_DECORATION_UNDERLINE | - NS_STYLE_TEXT_DECORATION_OVERLINE | - NS_STYLE_TEXT_DECORATION_LINE_THROUGH; - nsStyleContext* context = aStyleContext; - PRBool hasDecorations = context->HasTextDecorations(); - - while (hasDecorations) { - const nsStyleTextReset* styleText = context->GetStyleTextReset(); - if (!useOverride && - (NS_STYLE_TEXT_DECORATION_OVERRIDE_ALL & - styleText->mTextDecoration)) { - // This handles the La - // la la case. The link underline should be green. - useOverride = PR_TRUE; - overrideColor = context->GetStyleColor()->mColor; - } - - PRUint8 useDecorations = decorMask & styleText->mTextDecoration; - if (useDecorations) {// a decoration defined here - nscolor color = context->GetStyleColor()->mColor; - - if (NS_STYLE_TEXT_DECORATION_UNDERLINE & useDecorations) { - underColor = useOverride ? overrideColor : color; - decorMask &= ~NS_STYLE_TEXT_DECORATION_UNDERLINE; - decorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE; - } - if (NS_STYLE_TEXT_DECORATION_OVERLINE & useDecorations) { - overColor = useOverride ? overrideColor : color; - decorMask &= ~NS_STYLE_TEXT_DECORATION_OVERLINE; - decorations |= NS_STYLE_TEXT_DECORATION_OVERLINE; - } - if (NS_STYLE_TEXT_DECORATION_LINE_THROUGH & useDecorations) { - strikeColor = useOverride ? overrideColor : color; - decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_THROUGH; - decorations |= NS_STYLE_TEXT_DECORATION_LINE_THROUGH; - } - } - if (0 == decorMask) - break; - context = context->GetParent(); - if (!context) - break; - hasDecorations = context->HasTextDecorations(); - } - - nscoord offset; - nscoord size; - nscoord baseline = mAscent; - if (decorations & (NS_FONT_DECORATION_OVERLINE | - NS_FONT_DECORATION_UNDERLINE)) { - aTextStyle.mNormalFont->GetUnderline(offset, size); - if (decorations & NS_FONT_DECORATION_OVERLINE) { - aRenderingContext.SetColor(overColor); - aRenderingContext.FillRect(aX, aY, aWidth, size); - } - if (decorations & NS_FONT_DECORATION_UNDERLINE) { - aRenderingContext.SetColor(underColor); - aRenderingContext.FillRect(aX, aY + baseline - offset, aWidth, size); - } - } - if (decorations & NS_FONT_DECORATION_LINE_THROUGH) { - aTextStyle.mNormalFont->GetStrikeout(offset, size); - aRenderingContext.SetColor(strikeColor); - aRenderingContext.FillRect(aX, aY + baseline - offset, aWidth, size); - } - } - - if (aDetails){ - nsRect rect = GetRect(); - while(aDetails){ - const nscoord* sp= aSpacing; - PRInt32 startOffset = 0; - PRInt32 textWidth = 0; - PRInt32 start = PR_MAX(0,(aDetails->mStart - (PRInt32)aIndex)); - PRInt32 end = PR_MIN((PRInt32)aLength,(aDetails->mEnd - (PRInt32)aIndex)); - PRInt32 i; - if ((start < end) && ((aLength - start) > 0)) - { - //aDetails allready processed to have offsets from frame start not content offsets - if (start < end){ - if (aLength == 1) - textWidth = aWidth; - else { - if (aDetails->mStart > 0){ - if (sp) - { - for (i = 0; i < start;i ++){ - startOffset += *sp ++; - } - } - else - aRenderingContext.GetWidth(aText, start, startOffset); - } - if (sp){ - for (i = start; i < end;i ++){ - textWidth += *sp ++; - } - } - else - aRenderingContext.GetWidth(aText + start, - PRUint32(end - start), textWidth); - } - - nscolor lineColor; - float relativeSize; - nscoord offset, size; - nscoord baseline = mAscent; - switch (aDetails->mType) { - case nsISelectionController::SELECTION_NORMAL: - break; - case nsISelectionController::SELECTION_SPELLCHECK: - aTextStyle.mNormalFont->GetUnderline(offset, size); - aRenderingContext.SetLineStyle(nsLineStyle_kDotted); - aRenderingContext.SetColor(NS_RGB(255,0,0)); - /* - * If the rendering context is drawing text from right to left, - * reverse the coordinates of the underline to match. - */ - if (aRightToLeftText) { - nscoord rightEdge = aX + aWidth; - aRenderingContext.DrawLine(rightEdge - textWidth - startOffset, - aY + baseline - offset, - rightEdge - startOffset, - aY + baseline - offset); - } - else { - aRenderingContext.DrawLine(aX + startOffset, - aY + baseline - offset, - aX + startOffset + textWidth, - aY + baseline - offset); - } - break; - case nsISelectionController::SELECTION_IME_RAWINPUT: - case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: - case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: - case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: - if (aTextStyle.GetIMEUnderline(aDetails->mType, - &lineColor, - &relativeSize)) { - aTextStyle.mNormalFont->GetUnderline(offset, size); - aRenderingContext.SetColor(lineColor); - /* - * If the rendering context is drawing text from right to left, - * reverse the coordinates of the underline to match. - */ - nscoord leftEdge = aRightToLeftText ? - aX + aWidth - startOffset - textWidth + size : - aX + startOffset + size; - aRenderingContext.FillRect(leftEdge, - aY + baseline - offset, - textWidth - 2 * size, - (nscoord)(relativeSize * size)); - } - break; - default: - NS_ASSERTION(0,"what type of selection do i not know about?"); - break; - } - } - } - aDetails = aDetails->mNext; - } - } -} - - - -nsresult -nsTextFrame::GetContentAndOffsetsForSelection(nsPresContext *aPresContext, nsIContent **aContent, PRInt32 *aOffset, PRInt32 *aLength) -{ - if (!aContent || !aOffset || !aLength) - return NS_ERROR_NULL_POINTER; - //ARE WE GENERATED?? - *aContent = nsnull; - *aOffset = mContentOffset; - *aLength = mContentLength; - nsIFrame *parent = GetParent(); - if (parent) - { - if ((mState & NS_FRAME_GENERATED_CONTENT) != 0)//parent is generated so so are we. - { - //we COULD check the previous sibling but I dont think that is reliable - *aContent = parent->GetContent(); - if(!*aContent) - return NS_ERROR_FAILURE; - NS_ADDREF(*aContent); - - //ARE WE A BEFORE FRAME? if not then we assume we are an after frame. this may be bad later - nsIFrame *grandParent = parent->GetParent(); - if (grandParent) - { - nsIFrame *firstParent = grandParent->GetFirstChild(nsnull); - if (firstParent) - { - *aLength = 0; - if (firstParent == parent) //then our parent is the first child of granddad. use BEFORE - { - *aOffset = 0; - } - else - { - *aOffset = (*aContent)->GetChildCount(); - } - } - else - return NS_OK; - } - } - } - //END GENERATED BLOCK - if (!*aContent) - { - *aContent = mContent; - NS_IF_ADDREF(*aContent); - } - - return NS_OK; -} - -//--------------------------------------------------------- -nsresult nsTextFrame::GetTextInfoForPainting(nsPresContext* aPresContext, - nsIPresShell** aPresShell, - nsISelectionController** aSelectionController, - PRBool& aDisplayingSelection, - PRBool& aIsPaginated, - PRBool& aIsSelected, - PRBool& aHideStandardSelection, - PRInt16& aSelectionValue) -{ - NS_ENSURE_ARG_POINTER(aPresContext); - NS_ENSURE_ARG_POINTER(aPresShell); - NS_ENSURE_ARG_POINTER(aSelectionController); - - //get the presshell - NS_IF_ADDREF(*aPresShell = aPresContext->GetPresShell()); - if (!*aPresShell) - return NS_ERROR_FAILURE; - - //get the selection controller - nsresult rv = GetSelectionController(aPresContext, aSelectionController); - if (NS_FAILED(rv) || !(*aSelectionController)) - return NS_ERROR_FAILURE; - - (*aSelectionController)->GetDisplaySelection(&aSelectionValue); - - if (aPresContext->IsRenderingOnlySelection()) { - aIsPaginated = PR_TRUE; - aDisplayingSelection = PR_TRUE; - } else { - aIsPaginated = PR_FALSE; - aDisplayingSelection = - (aSelectionValue > nsISelectionController::SELECTION_HIDDEN); - } - - PRInt16 textSel=0; - (*aSelectionController)->GetSelectionFlags(&textSel); - if (!(textSel & nsISelectionDisplay::DISPLAY_TEXT)) - aDisplayingSelection = PR_FALSE; - - // the spellcheck selection should be visible all the time - aHideStandardSelection = !aDisplayingSelection; - if (!aDisplayingSelection){ - nsCOMPtr spellcheckSelection; - (*aSelectionController)->GetSelection(nsISelectionController::SELECTION_SPELLCHECK, - getter_AddRefs(spellcheckSelection)); - if (spellcheckSelection){ - PRBool iscollapsed = PR_FALSE; - spellcheckSelection->GetIsCollapsed(&iscollapsed); - if (!iscollapsed) - aDisplayingSelection = PR_TRUE; - } - } - - // Transform text from content into renderable form - // XXX If the text fragment is already Unicode and the text wasn't - // transformed when we formatted it, then there's no need to do all - // this and we should just render the text fragment directly. See - // PaintAsciiText()... - nsIDocument *doc = (*aPresShell)->GetDocument(); - if (!doc) - return NS_ERROR_FAILURE; - - aIsSelected = (GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT; - - return NS_OK; -} - -nsresult -nsTextFrame::GetSelectionStatus(nsPresContext* aPresContext, - PRInt16& aSelectionValue) -{ - NS_ENSURE_ARG_POINTER(aPresContext); - - // get the selection controller - nsCOMPtr selectionController; - nsresult rv = GetSelectionController(aPresContext, - getter_AddRefs(selectionController)); - if (NS_FAILED(rv) || !selectionController) - return NS_ERROR_FAILURE; - - selectionController->GetDisplaySelection(&aSelectionValue); - - return NS_OK; -} - -PRBool -nsTextFrame::IsTextInSelection() -{ - nsCOMPtr selCon; - nsCOMPtr shell; - PRBool displaySelection; - PRBool isPaginated; - PRBool isSelected; - PRBool hideStandardSelection; - PRInt16 selectionValue; - nsPresContext* presContext = PresContext(); - if (NS_FAILED(GetTextInfoForPainting(presContext, - getter_AddRefs(shell), - getter_AddRefs(selCon), - displaySelection, - isPaginated, - isSelected, - hideStandardSelection, - selectionValue))) { - return PR_FALSE; - } - - // Make enough space to transform - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) { - return PR_FALSE; - } - - // Transform text from content into renderable form - // XXX If the text fragment is already Unicode and the text wasn't - // transformed when we formatted it, then there's no need to do all - // this and we should just render the text fragment directly. See - // PaintAsciiText()... - - nsTextTransformer tx(presContext); - PRInt32 textLength; - // no need to worry about justification, that's always on the slow path - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); - - PRInt32* ip = indexBuffer.mBuffer; - PRUnichar* text = paintBuffer.mBuffer; - - isSelected = PR_FALSE; - if (0 != textLength) { - - SelectionDetails *details = nsnull; - - nsCOMPtr content; - PRInt32 offset; - PRInt32 length; - - nsresult rv = GetContentAndOffsetsForSelection(presContext, - getter_AddRefs(content), - &offset, &length); - if (NS_SUCCEEDED(rv) && content) { - details = GetFrameSelection()->LookUpSelection(content, mContentOffset, - mContentLength, PR_FALSE); - } - - //where are the selection points "really" - SelectionDetails *sdptr = details; - while (sdptr){ - sdptr->mStart = ip[sdptr->mStart] - mContentOffset; - sdptr->mEnd = ip[sdptr->mEnd] - mContentOffset; - sdptr = sdptr->mNext; - } - //while we have substrings... - //PRBool drawn = PR_FALSE; - DrawSelectionIterator iter(details, text, (PRUint32)textLength, nsnull, - nsTextPaintStyle::eNormalSelection); - if (!iter.IsDone() && iter.First()) { - isSelected = PR_TRUE; - } - - sdptr = details; - if (details) { - while ((sdptr = details->mNext) != nsnull) { - delete details; - details = sdptr; - } - delete details; - } - } - return isSelected; -} - -PRBool -nsTextFrame::IsVisibleInSelection(nsISelection* aSelection) -{ - // Check the quick way first - PRBool isSelected = (mState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT; - if (!isSelected) - return PR_FALSE; - - return IsTextInSelection(); -} - -void -nsTextFrame::PaintUnicodeText(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsTextPaintStyle& aTextStyle, - nscoord dx, nscoord dy) -{ - nsCOMPtr selCon; - nsCOMPtr shell; - PRBool displaySelection,canDarkenColor=PR_FALSE; - PRBool isPaginated; - PRBool isSelected; - PRBool hideStandardSelection; - PRInt16 selectionValue; -#ifdef IBMBIDI - PRBool isOddLevel = PR_FALSE; -#endif - - if (NS_FAILED(GetTextInfoForPainting(aPresContext, - getter_AddRefs(shell), - getter_AddRefs(selCon), - displaySelection, - isPaginated, - isSelected, - hideStandardSelection, - selectionValue))) { - return; - } - - if(isPaginated){ - canDarkenColor = CanDarken(aPresContext); - } - - // Make enough space to transform - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - if (displaySelection) { - if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) { - return; - } - } - nscoord width = mRect.width; - - // Transform text from content into renderable form - // XXX If the text fragment is already Unicode and the text wasn't - // transformed when we formatted it, then there's no need to do all - // this and we should just render the text fragment directly. See - // PaintAsciiText()... - - nsTextTransformer tx(aPresContext); - PRInt32 textLength; - - // In whitespace:-moz-pre-wrap it's possible that we trimmed multiple - // spaces from the end of line because they did not fit in the line. - // In that case, all those spaces need to be removed before painting. - PRBool removeMultipleTrimmedWS = NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == GetStyleText()->mWhiteSpace; - - // no need to worry about justification, that's always on the slow path - PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull), - &paintBuffer, &textLength, PR_FALSE, nsnull, removeMultipleTrimmedWS); - - PRInt32* ip = indexBuffer.mBuffer; - PRUnichar* text = paintBuffer.mBuffer; - - if (0 != textLength) - { -#ifdef IBMBIDI - PRBool isRightToLeftOnBidiPlatform = PR_FALSE; - PRBool isBidiSystem = PR_FALSE; - nsCharType charType = eCharType_LeftToRight; - if (aPresContext->BidiEnabled()) { - isBidiSystem = aPresContext->IsBidiSystem(); - isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1; - charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsGkAtoms::charType)); - - isRightToLeftOnBidiPlatform = (isBidiSystem && - (eCharType_RightToLeft == charType || - eCharType_RightToLeftArabic == charType)); - if (isRightToLeftOnBidiPlatform) { - // indicate that the platform should use its native - // capabilities to reorder the text with right-to-left - // base direction - aRenderingContext.SetRightToLeftText(PR_TRUE); - } - nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils(); - if (bidiUtils) { -#ifdef DEBUG - PRInt32 rememberTextLength = textLength; -#endif - PRUint32 hints = 0; - aRenderingContext.GetHints(hints); - bidiUtils->ReorderUnicodeText(text, textLength, - charType, isOddLevel, isBidiSystem, - (hints & NS_RENDERING_HINT_NEW_TEXT_RUNS) != 0); - NS_ASSERTION(rememberTextLength == textLength, "Bidi formatting changed text length"); - } - } -#endif // IBMBIDI - if (!displaySelection || !isSelected ) //draw text normally - { - // When there is no selection showing, use the fastest and - // simplest rendering approach - - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor)); - aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent); - PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext, - aTextStyle, dx, dy, width, PR_FALSE); - } - else - { //we draw according to selection rules - SelectionDetails *details = nsnull; - nsCOMPtr content; - PRInt32 offset; - PRInt32 length; - nsresult rv = GetContentAndOffsetsForSelection(aPresContext, - getter_AddRefs(content), - &offset, &length); - if (NS_SUCCEEDED(rv) && content) { - details = GetFrameSelection()->LookUpSelection(content, mContentOffset, - mContentLength, PR_FALSE); - } - - //where are the selection points "really" - SelectionDetails *sdptr = details; - while (sdptr){ - sdptr->mStart = ip[sdptr->mStart] - mContentOffset; - sdptr->mEnd = ip[sdptr->mEnd] - mContentOffset; -#ifdef SUNCTL - nsCOMPtr ctlObj; - ctlObj = do_CreateInstance(kLECID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Cell based cursor movement will not be supported\n"); - ctlObj = nsnull; - } - else { - PRInt32 start, end; - PRBool needsCTL = PR_FALSE; - - ctlObj->NeedsCTLFix(text, sdptr->mStart, sdptr->mEnd, &needsCTL); - - if (needsCTL && (sdptr->mEnd < textLength)) { - ctlObj->GetRangeOfCluster(text, PRInt32(textLength), sdptr->mEnd, - &start, &end); - if (sdptr->mStart > sdptr->mEnd) /* Left Edge */ - sdptr->mEnd = start; - else - sdptr->mEnd = end; - } - - /* Always start selection from a Right Edge */ - if (needsCTL && (sdptr->mStart > 0)) { - ctlObj->GetRangeOfCluster(text, PRInt32(textLength), - sdptr->mStart, &start, &end); - sdptr->mStart = end; - } - } -#endif /* SUNCTL */ -#ifdef IBMBIDI - AdjustSelectionPointsForBidi(sdptr, textLength, CHARTYPE_IS_RTL(charType), isOddLevel, isBidiSystem); -#endif - sdptr = sdptr->mNext; - } - if (!hideStandardSelection || displaySelection) { - /* - * Text is drawn by drawing the entire string every time, but - * using clip regions to control which part of the text is shown - * (selected or unselected.) We do this because you can't - * assume that the layout of a part of text will be the same - * when it's drawn apart from the entire string. This is true - * in languages like arabic, where shaping affects entire words. - * Simply put: length("abcd") != length("ab") + length("cd") in - * some languages. - */ - - // See if this rendering backend supports getting cluster - // information. - PRUint32 clusterHint = 0; - aRenderingContext.GetHints(clusterHint); - clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS; - - //while we have substrings... - //PRBool drawn = PR_FALSE; - DrawSelectionIterator iter(details, text, (PRUint32)textLength, &aTextStyle, - nsTextPaintStyle::eAllSelections); - if (!iter.IsDone() && iter.First()) - { - nscoord currentX = dx; - nscoord newWidth;//temp -#ifdef IBMBIDI // Simon - display substrings RTL in RTL frame - nscoord FrameWidth = 0; - if (isRightToLeftOnBidiPlatform) - if (NS_SUCCEEDED(aRenderingContext.GetWidth(text, textLength, FrameWidth))) - currentX = dx + FrameWidth; -#endif - while (!iter.IsDone()) - { - PRUnichar *currenttext = iter.CurrentTextUnicharPtr(); - PRUint32 currentlength= iter.CurrentLength(); - nscolor currentFGColor, currentBKColor; - PRBool isCurrentBKColorTransparent; - - PRBool isSelection = iter.GetSelectionColors(¤tFGColor, - ¤tBKColor, - &isCurrentBKColorTransparent); - - if (currentlength > 0) - { - if (clusterHint) { - PRUint32 tmpWidth; - rv = aRenderingContext.GetRangeWidth(text, textLength, currenttext - text, - (currenttext - text) + currentlength, - tmpWidth); - newWidth = nscoord(tmpWidth); - } - else { - rv = aRenderingContext.GetWidth(currenttext, currentlength,newWidth); //ADJUST FOR CHAR SPACING - } - if (NS_SUCCEEDED(rv)) { - if (isRightToLeftOnBidiPlatform) - currentX -= newWidth; - if (isSelection && !isPaginated) - {//DRAW RECT HERE!!! - if (!isCurrentBKColorTransparent) { - aRenderingContext.SetColor(currentBKColor); - aRenderingContext.FillRect(currentX, dy, newWidth, mRect.height); - } - } - } - else { - newWidth = 0; - } - } - else { - newWidth = 0; - } - - aRenderingContext.PushState(); - - nsRect rect(currentX, dy, newWidth, mRect.height); - aRenderingContext.SetClipRect(rect, nsClipCombine_kIntersect); - - if (isPaginated && !iter.IsBeforeOrAfter()) { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor)); - aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent); - } else if (!isPaginated) { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor,canDarkenColor)); - aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent); - } - - aRenderingContext.PopState(); - -#ifdef IBMBIDI - if (!isRightToLeftOnBidiPlatform) -#endif - currentX += newWidth; // increment twips X start - - iter.Next(); - } - } - else if (!isPaginated) - { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor)); - aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent); - } - } - PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext, - aTextStyle, dx, dy, width, - isRightToLeftOnBidiPlatform, text, details, 0, - (PRUint32)textLength, nsnull); - sdptr = details; - if (details){ - while ((sdptr = details->mNext) != nsnull) { - delete details; - details = sdptr; - } - delete details; - } - } -#ifdef IBMBIDI - if (isRightToLeftOnBidiPlatform) { - // indicate that future text should not be reordered with - // right-to-left base direction - aRenderingContext.SetRightToLeftText(PR_FALSE); - } -#endif // IBMBIDI - } -} - -//measure Spaced Textvoid -nsresult -nsTextFrame::GetPositionSlowly(nsIRenderingContext* aRendContext, - const nsPoint& aPoint, - nsIContent** aNewContent, - PRInt32& aOffset) - -{ - // pre-condition tests - NS_PRECONDITION(aRendContext && aNewContent, "null arg"); - if (!aRendContext || !aNewContent) { - return NS_ERROR_NULL_POINTER; - } - // initialize out param - *aNewContent = nsnull; - - nsPresContext* presContext = PresContext(); - nsTextStyle ts(presContext, *aRendContext, mStyleContext); - SetupTextRunDirection(presContext, aRendContext); - if (!ts.mSmallCaps && !ts.mWordSpacing && !ts.mLetterSpacing && !ts.mJustifying) { - return NS_ERROR_INVALID_ARG; - } - - /* This if clause is the cause of much pain. If aNewContent is set, then any - * code path that returns an error must set aNewContent to null before returning, - * or risk the caller unknowingly decrementing aNewContent inappropriately. - * Here's what Robert O'Callahan has to say on the matter: - If I'm not mistaken, in GetPositionSlowly, the values of aNewContent and - aOffset set in the conditional "if (aPoint.x - origin.x < 0)" are - overwritten on all successful return paths. Since they should never be - used by the caller if the function fails, that entire "if" statement is - --- or should be --- a no-op. Come to think of it, it doesn't make sense - either; setting aOffset to zero is nonsense. - - I recommend you just delete that "if" statement. - * - * If this clause is removed, then some of the bullet-proofing code - * prefaced with "bug 56704" comments can be removed as well. - */ - if (aPoint.x < 0) - { - *aNewContent = mContent; - aOffset =0; - } - - // Make enough space to transform - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - nsresult rv = indexBuffer.GrowTo(mContentLength + 1); - if (NS_FAILED(rv)) { - // If we've already assigned aNewContent, make sure to 0 it out here. - // See bug 56704. - *aNewContent = nsnull; - return rv; - } - - // Transform text from content into renderable form - nsTextTransformer tx(PresContext()); - PRInt32 textLength; - PRIntn numJustifiableCharacter; - - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength, PR_TRUE, &numJustifiableCharacter); - if (textLength <= 0) { - // If we've already assigned aNewContent, make sure to 0 it out here. - // aNewContent is undefined in the case that we return a failure, - // If we were to return a valid pointer, we risk decrementing that node's - // ref count an extra time by the caller. - // See bug 56704 for more details. - *aNewContent = nsnull; - return NS_ERROR_FAILURE; - } - -#ifdef IBMBIDI // Simon -- reverse RTL text here - PRBool isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1; - if (isOddLevel) { - PRUnichar *tStart, *tEnd; - PRUnichar tSwap; - for (tStart = paintBuffer.mBuffer, tEnd = tStart + textLength - 1; tEnd > tStart; tStart++, tEnd--) { - tSwap = *tStart; - *tStart = *tEnd; - *tEnd = tSwap; - } - } -#endif // IBMBIDI - - ComputeExtraJustificationSpacing(*aRendContext, ts, paintBuffer.mBuffer, textLength, numJustifiableCharacter); - { - //the following will first get the index into the PAINTBUFFER then the actual content - nscoord adjustedX = PR_MAX(0,aPoint.x); - -#ifdef IBMBIDI - if (isOddLevel) - aOffset = mContentOffset + textLength - - GetLengthSlowly(*aRendContext, ts, paintBuffer.mBuffer, - textLength, PR_TRUE, adjustedX); - else -#endif - aOffset = mContentOffset + - GetLengthSlowly(*aRendContext, ts,paintBuffer.mBuffer, - textLength, PR_TRUE, adjustedX); - PRInt32 i; - PRInt32* ip = indexBuffer.mBuffer; - for (i = 0;i <= mContentLength; i ++){ - if ((ip[i] >= aOffset) && //reverse mapping - (! NS_IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) { - aOffset = i + mContentOffset; - break; - } - } - } - - *aNewContent = mContent; - if (*aNewContent) - (*aNewContent)->AddRef(); - return NS_OK; -} - -void -nsTextFrame::RenderString(nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsPresContext* aPresContext, - nsTextPaintStyle& aTextStyle, - PRBool aRightToLeftText, - PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame, - nscoord aX, nscoord aY, - nscoord aWidth, - SelectionDetails *aDetails /*=nsnull*/) -{ - PRUnichar buf[TEXT_BUF_SIZE]; - PRUnichar* bp0 = buf; - - nscoord spacingMem[TEXT_BUF_SIZE]; - nscoord* sp0 = spacingMem; - - PRBool spacing = (0 != aTextStyle.mLetterSpacing) || - (0 != aTextStyle.mWordSpacing) || aTextStyle.mJustifying; - - PRBool justifying = aTextStyle.mJustifying && - (aTextStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aTextStyle.mExtraSpacePerJustifiableCharacter != 0); - - PRBool isCJ = IsChineseJapaneseLangGroup(); - PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState); - - //German 0x00df might expand to "SS", but no need to count it for speed reason - if (aTextStyle.mSmallCaps) { - if (aLength*2 > TEXT_BUF_SIZE) { - bp0 = new PRUnichar[aLength*2]; - if (spacing) - sp0 = new nscoord[aLength*2]; - } - } - else if (aLength > TEXT_BUF_SIZE) { - bp0 = new PRUnichar[aLength]; - if (spacing) - sp0 = new nscoord[aLength]; - } - - PRUnichar* bp = bp0; - nscoord* sp = sp0; - - nsIFontMetrics* lastFont = aTextStyle.mLastFont; - PRInt32 pendingCount; - PRUnichar* runStart = bp; - nscoord charWidth, width = 0; - PRInt32 countSoFar = 0; - // Save the color we want to use for the text, since calls to - // PaintTextDecorations in this method will call SetColor() on the rendering - // context. - nscolor textColor; - aRenderingContext.GetColor(textColor); - for (; --aLength >= 0; aBuffer++) { - nsIFontMetrics* nextFont; - nscoord glyphWidth = 0; - PRUnichar ch = *aBuffer; - if (aTextStyle.mSmallCaps && - (IsLowerCase(ch) || (ch == kSZLIG))) { - nextFont = aTextStyle.mSmallFont; - } - else { - nextFont = aTextStyle.mNormalFont; - } - if (nextFont != lastFont) { - pendingCount = bp - runStart; - if (0 != pendingCount) { - // Render the text with the color specified first. - aRenderingContext.SetColor(textColor); - // Measure previous run of characters using the previous font - aRenderingContext.DrawString(runStart, pendingCount, - aX, aY + mAscent, -1, - spacing ? sp0 : nsnull); - - // Note: use aY not small-y so that decorations are drawn with - // respect to the normal-font not the current font. - PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext, - aTextStyle, aX, aY, width, - aRightToLeftText, runStart, aDetails, - countSoFar, pendingCount, spacing ? sp0 : nsnull); - countSoFar += pendingCount; - aWidth -= width; - aX += width; - runStart = bp = bp0; - sp = sp0; - width = 0; - } - aRenderingContext.SetFont(nextFont); - lastFont = nextFont; - } - if (nextFont == aTextStyle.mSmallFont) { - PRUnichar upper_ch; - // German szlig should be expanded to "SS". - if (ch == kSZLIG) - upper_ch = (PRUnichar)'S'; - else - upper_ch = ToUpperCase(ch); - aRenderingContext.GetWidth(upper_ch, charWidth); - glyphWidth += charWidth + aTextStyle.mLetterSpacing; - if (ch == kSZLIG) //add an additional 'S' here. - { - *bp++ = upper_ch; - if (spacing) - *sp++ = glyphWidth; - width += glyphWidth; - } - ch = upper_ch; - } - else if (ch == ' ') { - glyphWidth += aTextStyle.mSpaceWidth + aTextStyle.mWordSpacing + aTextStyle.mLetterSpacing; - } - else if (NS_IS_HIGH_SURROGATE(ch) && aLength > 0 && - NS_IS_LOW_SURROGATE(*(aBuffer+1))) { - - // special handling for surrogate pair - aRenderingContext.GetWidth(aBuffer, 2, charWidth); - glyphWidth += charWidth + aTextStyle.mLetterSpacing; - // copy the surrogate low - *bp++ = ch; - --aLength; - aBuffer++; - ch = *aBuffer; - // put the width into the space buffer - width += glyphWidth; - if (spacing) - *sp++ = glyphWidth; - // set the glyphWidth to 0 so the code later will - // set a 0 for one element in space array for surrogate low to 0 - glyphWidth = 0; - } - else { - aRenderingContext.GetWidth(ch, charWidth); - glyphWidth += charWidth + aTextStyle.mLetterSpacing; - } - if (justifying && (!isEndOfLine || aLength > 0) - && IsJustifiableCharacter(ch, isCJ)) { - glyphWidth += aTextStyle.mExtraSpacePerJustifiableCharacter; - if ((PRUint32)--aTextStyle.mNumJustifiableCharacterToRender - < (PRUint32)aTextStyle.mNumJustifiableCharacterReceivingExtraJot) { - glyphWidth++; - } - } - *bp++ = ch; - if (spacing) - *sp++ = glyphWidth; - width += glyphWidth; - } - pendingCount = bp - runStart; - if (0 != pendingCount) { - // Render the text with the color specified first. - aRenderingContext.SetColor(textColor); - // Measure previous run of characters using the previous font - aRenderingContext.DrawString(runStart, pendingCount, aX, aY + mAscent, -1, - spacing ? sp0 : nsnull); - - // Note: use aY not small-y so that decorations are drawn with - // respect to the normal-font not the current font. - PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext, - aTextStyle, aX, aY, aWidth, - aRightToLeftText, runStart, aDetails, - countSoFar, pendingCount, spacing ? sp0 : nsnull); - } - aTextStyle.mLastFont = lastFont; - - if (bp0 != buf) { - delete [] bp0; - } - if (sp0 != spacingMem) { - delete [] sp0; - } -} - -inline void -nsTextFrame::MeasureSmallCapsText(nsIRenderingContext* aRenderingContext, - nsTextStyle& aTextStyle, - PRUnichar* aWord, - PRInt32 aWordLength, - PRBool aIsEndOfFrame, - nsTextDimensions* aDimensionsResult) -{ - aDimensionsResult->Clear(); - GetTextDimensions(*aRenderingContext, aTextStyle, aWord, aWordLength, - aIsEndOfFrame, aDimensionsResult); - if (aTextStyle.mLastFont != aTextStyle.mNormalFont) { - aRenderingContext->SetFont(aTextStyle.mNormalFont); - aTextStyle.mLastFont = aTextStyle.mNormalFont; - } -} - - -PRInt32 -nsTextFrame::GetTextDimensionsOrLength(nsIRenderingContext& aRenderingContext, - nsTextStyle& aStyle, - PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame, - nsTextDimensions* aDimensionsResult, - PRBool aGetTextDimensions/* true=get dimensions false = return length up to aDimensionsResult.width size*/) -{ - PRUnichar *inBuffer = aBuffer; - PRInt32 length = aLength; - nsAutoTextBuffer dimensionsBuffer; - if (NS_FAILED(dimensionsBuffer.GrowTo(length))) { - aDimensionsResult->Clear(); - return 0; - } - PRUnichar* bp = dimensionsBuffer.mBuffer; - - nsIFontMetrics* lastFont = aStyle.mLastFont; - nsTextDimensions sum, glyphDimensions; - PRBool justifying = aStyle.mJustifying && - (aStyle.mNumJustifiableCharacterReceivingExtraJot != 0 || aStyle.mExtraSpacePerJustifiableCharacter != 0); - PRBool isCJ = IsChineseJapaneseLangGroup(); - PRBool isEndOfLine = aIsEndOfFrame && IsEndOfLine(mState); - - for (PRInt32 prevLength = length; --length >= 0; prevLength = length) { - PRUnichar ch = *inBuffer++; - if (aStyle.mSmallCaps && - (IsLowerCase(ch) || (ch == kSZLIG))) { - PRUnichar upper_ch; - // German szlig should be expanded to "SS". - if (ch == kSZLIG) - upper_ch = (PRUnichar)'S'; - else - upper_ch = ToUpperCase(ch); - if (lastFont != aStyle.mSmallFont) { - lastFont = aStyle.mSmallFont; - aRenderingContext.SetFont(lastFont); - } - aRenderingContext.GetTextDimensions(&upper_ch, (PRUint32)1, glyphDimensions); - glyphDimensions.width += aStyle.mLetterSpacing; - if (ch == kSZLIG) - glyphDimensions.width += glyphDimensions.width; - } - else if (ch == ' ' || ch == CH_NBSP) { - glyphDimensions.width = aStyle.mSpaceWidth + aStyle.mLetterSpacing - + aStyle.mWordSpacing; - } - else { - if (lastFont != aStyle.mNormalFont) { - lastFont = aStyle.mNormalFont; - aRenderingContext.SetFont(lastFont); - } - if (NS_IS_HIGH_SURROGATE(ch) && length > 0 && - NS_IS_LOW_SURROGATE(*inBuffer)) { - aRenderingContext.GetTextDimensions(inBuffer-1, (PRUint32)2, glyphDimensions); - length--; - inBuffer++; - } else { - aRenderingContext.GetTextDimensions(&ch, (PRUint32)1, glyphDimensions); - } - glyphDimensions.width += aStyle.mLetterSpacing; - } - if (justifying && (!isEndOfLine || length > 0) - && IsJustifiableCharacter(ch, isCJ)) { - glyphDimensions.width += aStyle.mExtraSpacePerJustifiableCharacter; - if ((PRUint32)--aStyle.mNumJustifiableCharacterToMeasure - < (PRUint32)aStyle.mNumJustifiableCharacterReceivingExtraJot) { - ++glyphDimensions.width; - } - } - sum.Combine(glyphDimensions); - *bp++ = ch; - if (!aGetTextDimensions && sum.width >= aDimensionsResult->width) { - PRInt32 result = aLength - length; - if (2*(sum.width - aDimensionsResult->width) > glyphDimensions.width) //then we have gone too far, back up 1 - result = aLength - prevLength; - aStyle.mLastFont = lastFont; - return result; - } - } - aStyle.mLastFont = lastFont; - *aDimensionsResult = sum; - return aLength; -} - - -// XXX factor in logic from RenderString into here; gaps, justification, etc. -void -nsTextFrame::GetTextDimensions(nsIRenderingContext& aRenderingContext, - nsTextStyle& aTextStyle, - PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame, - nsTextDimensions* aDimensionsResult) -{ - GetTextDimensionsOrLength(aRenderingContext,aTextStyle, - aBuffer,aLength,aIsEndOfFrame,aDimensionsResult,PR_TRUE); -} - -PRInt32 -nsTextFrame::GetLengthSlowly(nsIRenderingContext& aRenderingContext, - nsTextStyle& aStyle, - PRUnichar* aBuffer, PRInt32 aLength, PRBool aIsEndOfFrame, - nscoord aWidth) -{ - nsTextDimensions dimensions; - dimensions.width = aWidth; - return GetTextDimensionsOrLength(aRenderingContext,aStyle, - aBuffer,aLength,aIsEndOfFrame,&dimensions,PR_FALSE); -} - -void -nsTextFrame::ComputeExtraJustificationSpacing(nsIRenderingContext& aRenderingContext, - nsTextStyle& aTextStyle, - PRUnichar* aBuffer, PRInt32 aLength, - PRInt32 aNumJustifiableCharacter) -{ - if (aTextStyle.mJustifying) { - nsTextDimensions trueDimensions; - - // OK, so this is a bit ugly. The problem is that to get the right margin - // nice and clean, we have to apply a little extra space to *some* of the - // justifiable characters. It has to be the same ones every time or things will go haywire. - // This implies that the GetTextDimensionsOrLength and RenderString functions depend - // on a little bit of secret state: which part of the prepared text they are - // looking at. It turns out that they get called in a regular way: they look - // at the text from the beginning to the end. So we just count which justifiable character - // we're up to, for each context. - // This is not a great solution, but a perfect solution requires much more - // widespread changes, to explicitly annotate all the transformed text fragments - // that are passed around with their position in the transformed text - // for the entire frame. - aTextStyle.mNumJustifiableCharacterToMeasure = 0; - aTextStyle.mExtraSpacePerJustifiableCharacter = 0; - aTextStyle.mNumJustifiableCharacterReceivingExtraJot = 0; - - GetTextDimensions(aRenderingContext, aTextStyle, aBuffer, aLength, PR_TRUE, &trueDimensions); - - aTextStyle.mNumJustifiableCharacterToMeasure = aNumJustifiableCharacter; - aTextStyle.mNumJustifiableCharacterToRender = aNumJustifiableCharacter; - - nscoord extraSpace = mRect.width - trueDimensions.width; - - if (extraSpace > 0 && aNumJustifiableCharacter > 0) { - aTextStyle.mExtraSpacePerJustifiableCharacter = extraSpace/aNumJustifiableCharacter; - aTextStyle.mNumJustifiableCharacterReceivingExtraJot = - extraSpace - aTextStyle.mExtraSpacePerJustifiableCharacter*aNumJustifiableCharacter; - } - } -} - -void -nsTextFrame::PaintTextSlowly(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsTextPaintStyle& aTextStyle, - nscoord dx, nscoord dy) -{ - nsCOMPtr selCon; - nsCOMPtr shell; - PRBool displaySelection; - PRBool isPaginated,canDarkenColor=PR_FALSE; - PRBool isSelected; - PRBool hideStandardSelection; - PRInt16 selectionValue; - if (NS_FAILED(GetTextInfoForPainting(aPresContext, - getter_AddRefs(shell), - getter_AddRefs(selCon), - displaySelection, - isPaginated, - isSelected, - hideStandardSelection, - selectionValue))) { - return; - } - - - if(isPaginated){ - canDarkenColor = CanDarken(aPresContext); - } - - // Make enough space to transform - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) { - return; - } - nscoord width = mRect.width; - PRInt32 textLength; - - nsTextTransformer tx(aPresContext); - PRIntn numJustifiableCharacter; - - PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull), - &paintBuffer, &textLength, PR_TRUE, &numJustifiableCharacter); - - PRInt32* ip = indexBuffer.mBuffer; - PRUnichar* text = paintBuffer.mBuffer; - - if (0 != textLength) { -#ifdef IBMBIDI - PRBool isRightToLeftOnBidiPlatform = PR_FALSE; - PRBool isBidiSystem = PR_FALSE; - PRBool isOddLevel = PR_FALSE; - PRUint32 hints = 0; - aRenderingContext.GetHints(hints); - PRBool paintCharByChar = (0 == (hints & NS_RENDERING_HINT_REORDER_SPACED_TEXT)) && - ((0 != aTextStyle.mLetterSpacing) || - (0 != aTextStyle.mWordSpacing) || - aTextStyle.mJustifying); - nsCharType charType = eCharType_LeftToRight; - - if (aPresContext->BidiEnabled()) { - isBidiSystem = aPresContext->IsBidiSystem(); - nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils(); - - if (bidiUtils) { - isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1; - charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsGkAtoms::charType)); -#ifdef DEBUG - PRInt32 rememberTextLength = textLength; -#endif - isRightToLeftOnBidiPlatform = (!paintCharByChar && - isBidiSystem && - (eCharType_RightToLeft == charType || - eCharType_RightToLeftArabic == charType)); - if (isRightToLeftOnBidiPlatform) { - // indicate that the platform should use its native - // capabilities to reorder the text with right-to-left - // base direction - aRenderingContext.SetRightToLeftText(PR_TRUE); - } - PRUint32 hints = 0; - aRenderingContext.GetHints(hints); - // If we will be painting char by char, handle the text like on non-bidi platform - bidiUtils->ReorderUnicodeText(text, textLength, charType, - isOddLevel, (paintCharByChar) ? PR_FALSE : isBidiSystem, - (hints & NS_RENDERING_HINT_NEW_TEXT_RUNS) != 0); - NS_ASSERTION(rememberTextLength == textLength, "Bidi formatting changed text length"); - } - } -#endif // IBMBIDI - ComputeExtraJustificationSpacing(aRenderingContext, aTextStyle, text, textLength, numJustifiableCharacter); - if (!displaySelection || !isSelected) { - // When there is no selection showing, use the fastest and - // simplest rendering approach - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor)); - RenderString(aRenderingContext, aStyleContext, aPresContext, aTextStyle, - PR_FALSE, text, textLength, PR_TRUE, dx, dy, width); - } - else - { - SelectionDetails *details = nsnull; - nsCOMPtr content; - PRInt32 offset; - PRInt32 length; - nsresult rv = GetContentAndOffsetsForSelection(aPresContext, - getter_AddRefs(content), - &offset, &length); - if (NS_SUCCEEDED(rv)) { - details = GetFrameSelection()->LookUpSelection(content, mContentOffset, - mContentLength, PR_FALSE); - } - - //where are the selection points "really" - SelectionDetails *sdptr = details; - while (sdptr){ - sdptr->mStart = ip[sdptr->mStart] - mContentOffset; - sdptr->mEnd = ip[sdptr->mEnd] - mContentOffset; -#ifdef IBMBIDI - AdjustSelectionPointsForBidi(sdptr, textLength, - CHARTYPE_IS_RTL(charType), isOddLevel, - (paintCharByChar) ? PR_FALSE : isBidiSystem); -#endif - sdptr = sdptr->mNext; - } - - DrawSelectionIterator iter(details, text, (PRUint32)textLength, &aTextStyle, - nsTextPaintStyle::eAllSelections); - if (!iter.IsDone() && iter.First()) - { - nscoord currentX = dx; - nsTextDimensions newDimensions;//temp -#ifdef IBMBIDI // Simon - display substrings RTL in RTL frame - if (isRightToLeftOnBidiPlatform) - currentX = dx + mRect.width; -#endif - while (!iter.IsDone()) - { - PRUnichar *currenttext = iter.CurrentTextUnicharPtr(); - PRUint32 currentlength= iter.CurrentLength(); - nscolor currentFGColor, currentBKColor; - PRBool isCurrentBKColorTransparent; - PRBool isSelection = iter.GetSelectionColors(¤tFGColor, - ¤tBKColor, - &isCurrentBKColorTransparent); - PRBool isEndOfFrame = iter.IsLast(); - GetTextDimensions(aRenderingContext, aTextStyle, currenttext, - (PRInt32)currentlength, isEndOfFrame, &newDimensions); - if (newDimensions.width) - { -#ifdef IBMBIDI - if (isRightToLeftOnBidiPlatform) - currentX -= newDimensions.width; -#endif - if (isSelection && !isPaginated) - {//DRAW RECT HERE!!! - if (!isCurrentBKColorTransparent) { - aRenderingContext.SetColor(currentBKColor); - aRenderingContext.FillRect(currentX, dy, newDimensions.width, mRect.height); - } - } - } - - if (isPaginated && !iter.IsBeforeOrAfter()) { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor, canDarkenColor)); - RenderString(aRenderingContext, aStyleContext, aPresContext, - aTextStyle, isRightToLeftOnBidiPlatform, - currenttext, currentlength, isEndOfFrame, - currentX, dy, newDimensions.width, details); - } else if (!isPaginated) { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor, canDarkenColor)); - RenderString(aRenderingContext,aStyleContext, aPresContext, - aTextStyle, isRightToLeftOnBidiPlatform, - currenttext, currentlength, isEndOfFrame, - currentX, dy, newDimensions.width, details); - } - -#ifdef IBMBIDI - if (!isRightToLeftOnBidiPlatform) -#endif - // increment twips X start but remember to get ready for - // next draw by reducing current x by letter spacing amount - currentX += newDimensions.width; // + aTextStyle.mLetterSpacing; - - iter.Next(); - } - } - else if (!isPaginated) - { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor)); - RenderString(aRenderingContext, aStyleContext, aPresContext, - aTextStyle, isRightToLeftOnBidiPlatform, text, - PRUint32(textLength), PR_TRUE, dx, dy, width, details); - } - sdptr = details; - if (details){ - while ((sdptr = details->mNext) != nsnull) { - delete details; - details = sdptr; - } - delete details; - } - } -#ifdef IBMBIDI - if (isRightToLeftOnBidiPlatform) { - // indicate that future text should not be reordered with - // right-to-left base direction - aRenderingContext.SetRightToLeftText(PR_FALSE); - } -#endif // IBMBIDI - } -} - -void -nsTextFrame::PaintAsciiText(nsPresContext* aPresContext, - nsIRenderingContext& aRenderingContext, - nsStyleContext* aStyleContext, - nsTextPaintStyle& aTextStyle, - nscoord dx, nscoord dy) -{ - NS_PRECONDITION(0 == (TEXT_HAS_MULTIBYTE & mState), "text is multi-byte"); - - nsCOMPtr selCon; - nsCOMPtr shell; - PRBool displaySelection,canDarkenColor=PR_FALSE; - PRBool isPaginated; - PRBool isSelected; - PRBool hideStandardSelection; - PRInt16 selectionValue; - if (NS_FAILED(GetTextInfoForPainting(aPresContext, - getter_AddRefs(shell), - getter_AddRefs(selCon), - displaySelection, - isPaginated, - isSelected, - hideStandardSelection, - selectionValue))) { - return; - } - - if(isPaginated){ - canDarkenColor = CanDarken(aPresContext); - } - - // Get the text fragment - const nsTextFragment* frag = mContent->GetText(); - if (!frag) { - return; - } - - // Make enough space to transform - nsAutoTextBuffer unicodePaintBuffer; - nsAutoIndexBuffer indexBuffer; - if (displaySelection) { - if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) { - return; - } - } - - nsTextTransformer tx(aPresContext); - - // See if we need to transform the text. If the text fragment is ascii and - // wasn't transformed, then we can skip this step. If we're displaying the - // selection and the text is selected, then we need to do this step so we - // can create the index buffer - PRInt32 textLength = 0; - const char* text; - char paintBufMem[TEXT_BUF_SIZE]; - char* paintBuf = paintBufMem; - if (frag->Is2b() || - (0 != (mState & TEXT_WAS_TRANSFORMED)) || - (displaySelection && isSelected)) { - - // Transform text from content into Unicode renderable form - // XXX If the text fragment is ascii, then we should ask the - // text transformer to leave the text in ascii. That way we can - // elimninate the conversion from Unicode back to ascii... - PrepareUnicodeText(tx, (displaySelection ? &indexBuffer : nsnull), - &unicodePaintBuffer, &textLength); - - - // Translate unicode data into ascii for rendering - if (textLength > TEXT_BUF_SIZE) { - paintBuf = new char[textLength]; - if (!paintBuf) { - return; - } - } - char* dst = paintBuf; - char* end = dst + textLength; - PRUnichar* src = unicodePaintBuffer.mBuffer; - while (dst < end) { - *dst++ = (char) ((unsigned char) *src++); - } - - text = paintBuf; - - } - else if (mContentOffset + mContentLength <= frag->GetLength()) { - text = frag->Get1b() + mContentOffset; - textLength = mContentLength; - - // See if we should skip leading whitespace - if (0 != (mState & TEXT_SKIP_LEADING_WS)) { - while ((textLength > 0) && XP_IS_SPACE(*text)) { - text++; - textLength--; - } - } - - // See if the text ends in a newline - if ((textLength > 0) && (text[textLength - 1] == '\n')) { - textLength--; - } - NS_ASSERTION(textLength >= 0, "bad text length"); - } - else { - // This might happen if a paint event beats the reflow; e.g., as - // is the case in bug 73291. Not a big deal, because the reflow - // will schedule another invalidate. - NS_WARNING("content length exceeds fragment length"); - } - - nscoord width = mRect.width; - PRInt32* ip = indexBuffer.mBuffer; - - if (0 != textLength) { - if (!displaySelection || !isSelected) { - //if selection is > content length then selection has "slid off" - // When there is no selection showing, use the fastest and - // simplest rendering approach - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor)); - aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent); - PaintTextDecorations(aRenderingContext, aStyleContext, - aPresContext, aTextStyle, dx, dy, width, PR_FALSE); - } - else { - SelectionDetails *details = nsnull; - nsCOMPtr content; - PRInt32 offset; - PRInt32 length; - nsresult rv = GetContentAndOffsetsForSelection(aPresContext, - getter_AddRefs(content), - &offset, &length); - if (NS_SUCCEEDED(rv)) { - details = GetFrameSelection()->LookUpSelection(content, mContentOffset, - mContentLength, PR_FALSE); - } - - //where are the selection points "really" - SelectionDetails *sdptr = details; - while (sdptr){ - sdptr->mStart = ip[sdptr->mStart] - mContentOffset; - sdptr->mEnd = ip[sdptr->mEnd] - mContentOffset; - sdptr = sdptr->mNext; - } - - if (!hideStandardSelection || displaySelection) { - //ITS OK TO CAST HERE THE RESULT WE USE WILLNOT DO BAD CONVERSION - DrawSelectionIterator iter(details, (PRUnichar *)text, - (PRUint32)textLength, &aTextStyle, - nsTextPaintStyle::eAllSelections); - - // See if this rendering backend supports getting cluster - // information. - PRUint32 clusterHint = 0; - aRenderingContext.GetHints(clusterHint); - clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS; - - if (!iter.IsDone() && iter.First()) - { - nscoord currentX = dx; - nscoord newWidth;//temp - while (!iter.IsDone()) - { - char *currenttext = iter.CurrentTextCStrPtr(); - PRUint32 currentlength= iter.CurrentLength(); - nscolor currentFGColor, currentBKColor; - PRBool isCurrentBKColorTransparent; - - if (clusterHint) { - PRUint32 tmpWidth; - rv = aRenderingContext.GetRangeWidth(text, textLength, currenttext - text, - (currenttext - text) + currentlength, - tmpWidth); - newWidth = nscoord(tmpWidth); - } - else { - rv = aRenderingContext.GetWidth(currenttext, currentlength,newWidth); //ADJUST FOR CHAR SPACING - } - - PRBool isSelection = iter.GetSelectionColors(¤tFGColor, - ¤tBKColor, - &isCurrentBKColorTransparent); - - if (NS_SUCCEEDED(rv)) { - if (isSelection && !isPaginated) - {//DRAW RECT HERE!!! - if (!isCurrentBKColorTransparent) { - aRenderingContext.SetColor(currentBKColor); - aRenderingContext.FillRect(currentX, dy, newWidth, mRect.height); - } - } - } - else { - newWidth =0; - } - - aRenderingContext.PushState(); - - nsRect rect(currentX, dy, newWidth, mRect.height); - aRenderingContext.SetClipRect(rect, nsClipCombine_kIntersect); - - if (isPaginated && !iter.IsBeforeOrAfter()) { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor)); - aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent); - } else if (!isPaginated) { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(currentFGColor,canDarkenColor)); - aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent); - } - - aRenderingContext.PopState(); - - currentX+=newWidth;//increment twips X start - - iter.Next(); - } - } - else if (!isPaginated) - { - aRenderingContext.SetColor(nsCSSRendering::TransformColor(aTextStyle.mColor->mColor,canDarkenColor)); - aRenderingContext.DrawString(text, PRUint32(textLength), dx, dy + mAscent); - } - } - - PaintTextDecorations(aRenderingContext, aStyleContext, aPresContext, - aTextStyle, dx, dy, width, PR_FALSE, - unicodePaintBuffer.mBuffer, - details, 0, textLength); - sdptr = details; - if (details){ - while ((sdptr = details->mNext) != nsnull) { - delete details; - details = sdptr; - } - delete details; - } - } - } - - // Cleanup - if (paintBuf != paintBufMem) { - delete [] paintBuf; - } -} - -// XXX I don't really want to rewrite GetPositionHelper, so I'm doing this -// hack for now -nsIFrame::ContentOffsets nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint) { - ContentOffsets offsets; - GetPositionHelper(aPoint, getter_AddRefs(offsets.content), offsets.offset, - offsets.secondaryOffset); - offsets.associateWithNext = (mContentOffset == offsets.offset); - return offsets; -} - -//--------------------------------------------------------------------------- -// Uses a binary search to find the position of the cursor in the text. -// The "indices array is used to map from the compressed text back to the -// un-compressed text, selection is based on the un-compressed text, the visual -// display of selection is based on the compressed text. -//--------------------------------------------------------------------------- -NS_IMETHODIMP -nsTextFrame::GetPositionHelper(const nsPoint& aPoint, - nsIContent ** aNewContent, - PRInt32& aContentOffset, - PRInt32& aContentOffsetEnd) - -{ - // pre-condition tests - NS_PRECONDITION(aNewContent, "null arg"); - if (!aNewContent) { - return NS_ERROR_NULL_POINTER; - } - // initialize out param - *aNewContent = nsnull; - - DEBUG_VERIFY_NOT_DIRTY(mState); - if (mState & NS_FRAME_IS_DIRTY) - return NS_ERROR_UNEXPECTED; - - nsPresContext *presContext = PresContext(); - nsIPresShell *shell = presContext->GetPresShell(); - if (shell) { - nsCOMPtr rendContext; - nsresult rv = shell->CreateRenderingContext(this, getter_AddRefs(rendContext)); - if (NS_SUCCEEDED(rv)) { - nsTextStyle ts(presContext, *rendContext, mStyleContext); - SetupTextRunDirection(presContext, rendContext); - if (ts.mSmallCaps || ts.mWordSpacing || ts.mLetterSpacing || ts.mJustifying) { - nsresult result = GetPositionSlowly(rendContext, aPoint, aNewContent, - aContentOffset); - aContentOffsetEnd = aContentOffset; - return result; - } - - // Make enough space to transform - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - rv = indexBuffer.GrowTo(mContentLength + 1); - if (NS_FAILED(rv)) { - return rv; - } - - // Find the font metrics for this text - nsLayoutUtils::SetFontFromStyle(rendContext, mStyleContext); - - // Get the renderable form of the text - nsTextTransformer tx(PresContext()); - PRInt32 textLength; - // no need to worry about justification, that's always on the slow path - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); - - if (textLength <= 0) { - aContentOffset = mContentOffset; - aContentOffsetEnd = aContentOffset; - } - else - { - PRInt32* ip = indexBuffer.mBuffer; - - PRInt32 indx; - PRInt32 textWidth = 0; - PRUnichar* text = paintBuffer.mBuffer; - - // See if the font backend will do all the hard work for us. - PRUint32 clusterHint = 0; - rendContext->GetHints(clusterHint); - clusterHint &= NS_RENDERING_HINT_TEXT_CLUSTERS; - if (clusterHint) { - indx = rendContext->GetPosition(text, textLength, aPoint); - } - else { -#ifdef IBMBIDI - PRBool getReversedPos = NS_GET_EMBEDDING_LEVEL(this) & 1; - nscoord posX = (getReversedPos) ? - (mRect.width) - (aPoint.x) : aPoint.x; - - PRBool found = nsLayoutUtils::BinarySearchForPosition(rendContext, text, 0, 0, 0, - PRInt32(textLength), - PRInt32(posX) , //go to local coordinates - indx, textWidth); - -#else - PRBool found = nsLayoutUtils::BinarySearchForPosition(rendContext, text, 0, 0, 0, - PRInt32(textLength), - PRInt32(aPoint.x) , //go to local coordinates - indx, textWidth); -#endif // IBMBIDI - if (found) { - PRInt32 charWidth; - if (NS_IS_HIGH_SURROGATE(text[indx])) - rendContext->GetWidth(&text[indx], 2, charWidth); - else - rendContext->GetWidth(text[indx], charWidth); - charWidth /= 2; - -#ifdef IBMBIDI - if (getReversedPos) { - if (mRect.width - aPoint.x> textWidth+charWidth ) { - indx++; - } - } - else -#endif // IBMBIDI - if ((aPoint.x) > textWidth+charWidth) { - indx++; - } - } - } - - aContentOffset = indx + mContentOffset; - //reusing wordBufMem - PRInt32 i; - for (i = 0; i < mContentLength; i ++){ - if ((ip[i] >= aContentOffset) && //reverse mapping - (! NS_IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) { - break; - } - } - aContentOffset = i + mContentOffset; -#ifdef IBMBIDI - PRInt32 bidiStopOffset = mContentOffset + mContentLength; - - if (aContentOffset >= mContentOffset && aContentOffset < bidiStopOffset) { - PRInt32 curindx = ip[aContentOffset - mContentOffset] - mContentOffset; - while (curindx < textLength && IS_BIDI_DIACRITIC(text[curindx])) { - if (++aContentOffset >= bidiStopOffset) - break; - curindx = ip[aContentOffset - mContentOffset] - mContentOffset; - } - } -#endif // IBMBIDI - aContentOffsetEnd = aContentOffset; - NS_ASSERTION(i<= mContentLength, "offset we got from binary search is messed up"); - } - *aNewContent = mContent; - if (*aNewContent) { - (*aNewContent)->AddRef(); - } - } - } - return NS_OK; -} - -// [HACK] Foward Declarations -void ForceDrawFrame(nsFrame * aFrame); - -//null range means the whole thing -NS_IMETHODIMP -nsTextFrame::SetSelected(nsPresContext* aPresContext, - nsIDOMRange *aRange, - PRBool aSelected, - nsSpread aSpread) -{ - DEBUG_VERIFY_NOT_DIRTY(mState); -#if 0 //XXXrbs disable due to bug 310318 - if (mState & NS_FRAME_IS_DIRTY) - return NS_ERROR_UNEXPECTED; -#endif - - if (aSelected && ParentDisablesSelection()) - return NS_OK; - -#if 0 - PRBool isSelected = ((GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT); - if (!aSelected && !isSelected) //already set thanks - { - return NS_OK; - } -#endif - - // check whether style allows selection - PRBool selectable; - IsSelectable(&selectable, nsnull); - if (!selectable) - return NS_OK;//do not continue no selection for this frame. - - PRBool found = PR_FALSE; - if (aRange) { - //lets see if the range contains us, if so we must redraw! - nsCOMPtr endNode; - PRInt32 endOffset; - nsCOMPtr startNode; - PRInt32 startOffset; - aRange->GetEndContainer(getter_AddRefs(endNode)); - aRange->GetEndOffset(&endOffset); - aRange->GetStartContainer(getter_AddRefs(startNode)); - aRange->GetStartOffset(&startOffset); - nsCOMPtr thisNode = do_QueryInterface(GetContent()); - - if (thisNode == startNode) - { - if ((mContentOffset + mContentLength) >= startOffset) - { - found = PR_TRUE; - if (thisNode == endNode) - { //special case - if (endOffset == startOffset) //no need to redraw since drawing takes place with cursor - found = PR_FALSE; - - if (mContentOffset > endOffset) - found = PR_FALSE; - } - } - } - else if (thisNode == endNode) - { - if (mContentOffset < endOffset) - found = PR_TRUE; - else - { - found = PR_FALSE; - } - } - else - { - found = PR_TRUE; - } - } - else { - // null range means the whole thing - found = PR_TRUE; - } - - if ( aSelected ) - AddStateBits(NS_FRAME_SELECTED_CONTENT); - else - {//we need to see if any other selection available. - SelectionDetails *details = nsnull; - nsCOMPtr content; - PRInt32 offset; - PRInt32 length; - - nsresult rv = GetContentAndOffsetsForSelection(aPresContext, - getter_AddRefs(content), - &offset, &length); - if (NS_SUCCEEDED(rv) && content) { - details = GetFrameSelection()->LookUpSelection(content, offset, - length, PR_TRUE); - // PR_TRUE last param used here! we need to see if we are still selected. so no shortcut - } - if (!details) - RemoveStateBits(NS_FRAME_SELECTED_CONTENT); - else - { - SelectionDetails *sdptr = details; - while ((sdptr = details->mNext) != nsnull) { - delete details; - details = sdptr; - } - delete details; - } - } - if (found){ //if range contains this frame... - // Selection might change our border, content and outline appearance - // But textframes can't have an outline. So just use the simple - // bounds - Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_FALSE); - } - if (aSpread == eSpreadDown) - { - nsIFrame* frame = GetPrevContinuation(); - while(frame){ - frame->SetSelected(aPresContext, aRange,aSelected,eSpreadNone); - frame = frame->GetPrevContinuation(); - } - frame = GetNextContinuation(); - while (frame){ - frame->SetSelected(aPresContext, aRange,aSelected,eSpreadNone); - frame = frame->GetNextContinuation(); - } - } - return NS_OK; -} - -NS_IMETHODIMP -nsTextFrame::GetPointFromOffset(nsPresContext* aPresContext, - nsIRenderingContext* inRendContext, - PRInt32 inOffset, - nsPoint* outPoint) -{ - if (!aPresContext || !inRendContext || !outPoint) - return NS_ERROR_NULL_POINTER; - - outPoint->x = 0; - outPoint->y = 0; - - DEBUG_VERIFY_NOT_DIRTY(mState); - if (mState & NS_FRAME_IS_DIRTY) - return NS_ERROR_UNEXPECTED; - - if (mContentLength <= 0) { - return NS_OK; - } - - inOffset-=mContentOffset; - if (inOffset < 0){ - NS_ASSERTION(0,"offset less than this frame has in GetPointFromOffset"); - inOffset = 0; - } - if (inOffset >= mContentLength) - inOffset = mContentLength; - - nsTextStyle ts(aPresContext, *inRendContext, mStyleContext); - SetupTextRunDirection(aPresContext, inRendContext); - - // Make enough space to transform - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - nsresult rv = indexBuffer.GrowTo(mContentLength + 1); - if (NS_FAILED(rv)) { - return rv; - } - - // Transform text from content into renderable form - nsTextTransformer tx(aPresContext); - PRInt32 textLength; - PRIntn numJustifiableCharacter; - - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength, PR_FALSE, &numJustifiableCharacter); - - ComputeExtraJustificationSpacing(*inRendContext, ts, paintBuffer.mBuffer, textLength, numJustifiableCharacter); - - - PRInt32* ip = indexBuffer.mBuffer; - if (inOffset > mContentLength){ - NS_ASSERTION(0, "invalid offset passed to GetPointFromOffset"); - inOffset = mContentLength; - } - - while (inOffset >=0 && ip[inOffset] < mContentOffset) //buffer has shrunk - inOffset --; - nscoord width = mRect.width; - if (inOffset <0) - { - NS_ASSERTION(0, "invalid offset passed to GetPointFromOffset"); - inOffset=0; - width = 0; - } - else - { - PRInt32 hitLength = ip[inOffset] - mContentOffset; - if (ts.mSmallCaps || (0 != ts.mWordSpacing) || (0 != ts.mLetterSpacing) || ts.mJustifying) - { - nsTextDimensions dimensions; - GetTextDimensions(*inRendContext, ts, paintBuffer.mBuffer, hitLength, - textLength == hitLength, &dimensions); - width = dimensions.width; - } - else - { - PRInt32 totalLength = mContent->TextLength(); // length up to the last-in-flow frame - if ((hitLength == textLength) && (inOffset == mContentLength) && - (mContentOffset + mContentLength == totalLength)) { - // no need to re-measure when at the end of the last-in-flow - } - else - inRendContext->GetWidth(paintBuffer.mBuffer, hitLength, width); - } - if ((hitLength == textLength && inOffset > 0 && ip[inOffset] == ip[inOffset-1]) - && (TEXT_TRIMMED_WS & mState)) { - // - // Offset must be after a space that has - // been trimmed off the end of the frame. - // Add the width of the trimmed space back - // to the total width, so the caret appears - // in the proper place! - // - // NOTE: the trailing whitespace includes the word and letter spacing!! - width += ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing; - } - } - if (NS_GET_EMBEDDING_LEVEL(this) & 1) - outPoint->x = mRect.width - width; - else - outPoint->x = width; - outPoint->y = 0; - - return NS_OK; -} - - - -NS_IMETHODIMP -nsTextFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset, - PRBool inHint, - PRInt32* outFrameContentOffset, - nsIFrame **outChildFrame) -{ - DEBUG_VERIFY_NOT_DIRTY(mState); -#if 0 //XXXrbs disable due to bug 310227 - if (mState & NS_FRAME_IS_DIRTY) - return NS_ERROR_UNEXPECTED; -#endif - - if (nsnull == outChildFrame) - return NS_ERROR_NULL_POINTER; - PRInt32 contentOffset = inContentOffset; - - if (contentOffset != -1) //-1 signified the end of the current content - contentOffset = inContentOffset - mContentOffset; - - if ((contentOffset > mContentLength) || ((contentOffset == mContentLength) && inHint) ) - { - //this is not the frame we are looking for. - nsIFrame* nextContinuation = GetNextContinuation(); - if (nextContinuation) - { - return nextContinuation->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame); - } - else { - if (contentOffset != mContentLength) //that condition was only for when there is a choice - return NS_ERROR_FAILURE; - } - } - - if (inContentOffset < mContentOffset) //could happen with floats! - { - *outChildFrame = GetPrevInFlow(); - if (*outChildFrame) - return (*outChildFrame)->GetChildFrameContainingOffset(inContentOffset, inHint, - outFrameContentOffset,outChildFrame); - else - return NS_OK; //this can't be the right thing to do? - } - - *outFrameContentOffset = contentOffset; - *outChildFrame = this; - return NS_OK; -} - -PRBool -nsTextFrame::PeekOffsetNoAmount(PRBool aForward, PRInt32* aOffset) -{ - NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range"); - return (!IsEmpty()); -} - -PRBool -nsTextFrame::PeekOffsetCharacter(PRBool aForward, PRInt32* aOffset) -{ - NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range"); - PRInt32 startOffset = *aOffset; - // A negative offset means "end of frame". - if (startOffset < 0) - startOffset = mContentLength; - - nsPresContext* presContext = PresContext(); - - // Transform text from content into renderable form - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - nsresult rv = indexBuffer.GrowTo(mContentLength + 1); - if (NS_FAILED(rv)) { - return PR_FALSE; - } - PRInt32 textLength; - nsTextTransformer tx(presContext); - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); - PRInt32* ip = indexBuffer.mBuffer; - - PRBool found = PR_TRUE; - - PRBool selectable; - PRUint8 selectStyle; - - IsSelectable(&selectable, &selectStyle); - if (selectStyle == NS_STYLE_USER_SELECT_ALL) - return PR_FALSE; - - if (!aForward) { - *aOffset = 0; - PRInt32 i; - - nsAutoPRUint8Buffer clusterBuffer; - rv = FillClusterBuffer(presContext, paintBuffer.mBuffer, - (PRUint32)textLength, clusterBuffer); - NS_ENSURE_SUCCESS(rv, rv); - - for (i = startOffset-1; i >= 0; i--){ - if ((ip[i] < ip[startOffset]) && - (clusterBuffer.mBuffer[ip[i] - mContentOffset]) && - (! NS_IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i]-mContentOffset]))) - { - *aOffset = i; - break; - } - } - -#ifdef SUNCTL - static NS_DEFINE_CID(kLECID, NS_ULE_CID); - - nsCOMPtr ctlObj; - ctlObj = do_CreateInstance(kLECID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Cell based cursor movement will not be supported\n"); - ctlObj = nsnull; - } - else { - PRBool needsCTL = PR_FALSE; - PRInt32 previousOffset; - - ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*, - paintBuffer.mBuffer), - mContentOffset + startOffset, -1, &needsCTL); - - if (needsCTL) { - ctlObj->PrevCluster(NS_REINTERPRET_CAST(const PRUnichar*, - paintBuffer.mBuffer), - textLength, mContentOffset + startOffset, - &previousOffset); - *aOffset = i = previousOffset - mContentOffset; - } - } -#endif /* SUNCTL */ - - if (i < 0) - found = PR_FALSE; - } - else // aForward - { - PRInt32 i; - *aOffset = mContentLength; - - nsAutoPRUint8Buffer clusterBuffer; - rv = FillClusterBuffer(presContext, paintBuffer.mBuffer, - (PRUint32)textLength, clusterBuffer); - NS_ENSURE_SUCCESS(rv, rv); - - for (i = startOffset; i <= mContentLength; i++) { - if ((ip[i] > ip[startOffset]) && - ((i == mContentLength) || - (!NS_IS_LOW_SURROGATE(paintBuffer.mBuffer[ip[i] - mContentOffset])) && - (clusterBuffer.mBuffer[ip[i] - mContentOffset]))) { - *aOffset = i; - break; - } - } - -#ifdef SUNCTL - static NS_DEFINE_CID(kLECID, NS_ULE_CID); - - nsCOMPtr ctlObj; - ctlObj = do_CreateInstance(kLECID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Cell based cursor movement will not be supported\n"); - ctlObj = nsnull; - } - else { - PRBool needsCTL = PR_FALSE; - PRInt32 nextOffset; - - ctlObj->NeedsCTLFix(NS_REINTERPRET_CAST(const PRUnichar*, - paintBuffer.mBuffer), - mContentOffset + startOffset, 0, &needsCTL); - - if (needsCTL) { - - ctlObj->NextCluster(NS_REINTERPRET_CAST(const PRUnichar*, - paintBuffer.mBuffer), - textLength, mContentOffset + startOffset, - &nextOffset); - *aOffset = i = nextOffset - mContentOffset; - } - } -#endif /* SUNCTL */ - - if (i > mContentLength) - found = PR_FALSE; - } - return found; -} - -PRBool -nsTextFrame::PeekOffsetWord(PRBool aForward, PRBool aWordSelectEatSpace, PRBool aIsKeyboardSelect, - PRInt32* aOffset, PRBool* aSawBeforeType) -{ - NS_ASSERTION (aOffset && *aOffset <= mContentLength, "aOffset out of range"); - PRInt32 startOffset = *aOffset; - if (startOffset < 0) - startOffset = mContentLength; - - nsTextTransformer tx(PresContext()); - 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? - PRBool found = PR_FALSE; - PRBool isWhitespace, wasTransformed; - PRInt32 wordLen, contentLen; - - PRBool selectable; - PRUint8 selectStyle; - IsSelectable(&selectable, &selectStyle); - if (selectStyle == NS_STYLE_USER_SELECT_ALL) - found = PR_FALSE; - else - { - tx.Init(this, mContent, mContentOffset + startOffset); - if (!aForward) { - *aOffset = 0; //initialize -#ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1; -#endif // IBMBIDI - if (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, - PR_FALSE, aIsKeyboardSelect) && - contentLen <= startOffset) { - if ((aWordSelectEatSpace ? isWhitespace : !isWhitespace) || !*aSawBeforeType){ - *aOffset = startOffset - contentLen; - keepSearching = PR_TRUE; - if (aWordSelectEatSpace ? isWhitespace : !isWhitespace) - *aSawBeforeType = PR_TRUE; -#ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1; -#endif // IBMBIDI - while (tx.GetPrevWord(PR_FALSE, &wordLen, &contentLen, - &isWhitespace, PR_FALSE, - aIsKeyboardSelect)){ - if (aWordSelectEatSpace ? !isWhitespace : *aSawBeforeType) - break; - if (contentLen >= startOffset) - goto TryNextFrame; - *aOffset -= contentLen; - if (aWordSelectEatSpace ? isWhitespace : !isWhitespace) - *aSawBeforeType = PR_TRUE; -#ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset : -1; -#endif // IBMBIDI - } - keepSearching = *aOffset <= 0; - if (!keepSearching) - found = PR_TRUE; - } - else { - *aOffset = mContentLength; - found = PR_TRUE; - } - } - } - else { // aForward - *aOffset = mContentLength; //initialize - -#ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1; -#endif // IBMBIDI - if (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aIsKeyboardSelect) && - (startOffset + contentLen <= mContentLength)) { - - if ((aWordSelectEatSpace ? isWhitespace : !isWhitespace) || !*aSawBeforeType) { - *aOffset = startOffset + contentLen; - keepSearching = PR_TRUE; - if (aWordSelectEatSpace ? isWhitespace : !isWhitespace) - *aSawBeforeType = PR_TRUE; -#ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1; -#endif // IBMBIDI - while (tx.GetNextWord(PR_FALSE, &wordLen, &contentLen, &isWhitespace, &wasTransformed, PR_TRUE, PR_FALSE, aIsKeyboardSelect)) - { - if (aWordSelectEatSpace ? !isWhitespace : *aSawBeforeType) - break; - if (startOffset + contentLen >= mContentLength) - goto TryNextFrame; - if (aWordSelectEatSpace ? isWhitespace : !isWhitespace) - *aSawBeforeType = PR_TRUE; - *aOffset += contentLen; -#ifdef IBMBIDI - wordLen = (mState & NS_FRAME_IS_BIDI) ? mContentOffset + mContentLength : -1; -#endif // IBMBIDI - } - keepSearching = *aOffset >= mContentLength; - if (!keepSearching) - found = PR_TRUE; - } - else - { - *aOffset = 0; - found = PR_TRUE; - } - } - } - } -TryNextFrame: - if (!found || - *aOffset > mContentLength || - *aOffset < 0) - { - found = PR_FALSE; - *aOffset = PR_MIN(*aOffset, mContentLength); - *aOffset = PR_MAX(*aOffset, 0); - } - return found; -} - -NS_IMETHODIMP -nsTextFrame::CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aRecurse, PRBool *aFinished, PRBool *_retval) -{ - if (!aFinished || !_retval) - return NS_ERROR_NULL_POINTER; - if (*aFinished) - return NS_ERROR_FAILURE; //dont call with finished != false - if (mContentOffset > aEndIndex) - return NS_OK; //reached the end - if (mContentOffset > aStartIndex) - aStartIndex = mContentOffset; - if (aStartIndex >= aEndIndex) //how can it be greater?? check anyway - return NS_OK; //reached the end. - - nsresult rv ; - if (aStartIndex < (mContentOffset + mContentLength)) - { - //get the presshell - nsIPresShell *shell = aContext->GetPresShell(); - if (!shell) - return NS_ERROR_FAILURE; - - //get the document - nsIDocument *doc = shell->GetDocument(); - if (!doc) - return NS_ERROR_FAILURE; - //create texttransformer - nsTextTransformer tx(aContext); - //create the buffers - nsAutoTextBuffer paintBuffer; - nsAutoIndexBuffer indexBuffer; - if (NS_FAILED(indexBuffer.GrowTo(mContentLength + 1))) - return NS_ERROR_FAILURE;//bail out - - PRInt32 textLength; - PrepareUnicodeText(tx, &indexBuffer, &paintBuffer, &textLength); - if (textLength)//we have something to measure? - { - PRInt32 start = PR_MAX(aStartIndex,mContentOffset); - PRInt32 end = PR_MIN(mContentOffset + mContentLength-1, aEndIndex); //base 0 index of array - while (start != end) - { - if (indexBuffer.mBuffer[start] < indexBuffer.mBuffer[start+1]) //we have a rendered char! - { - *aFinished = PR_TRUE;//we are done bubble out. - *_retval = PR_TRUE;//hit a drawn char - return NS_OK; - } - start++; - } - if (start == aEndIndex) - { - *aFinished = PR_TRUE; - } - } - } - if (aRecurse) //recurse through the siblings. - { - nsIFrame *nextInFlow = this; - rv = NS_OK; - while (!aFinished && nextInFlow && NS_SUCCEEDED(rv) && !*_retval) //while we havent found anything visible - { - nextInFlow = nextInFlow->GetNextInFlow(); - if (nextInFlow) - { - rv = nextInFlow->CheckVisibility(aContext,aStartIndex,aEndIndex,PR_FALSE,aFinished,_retval); - } - } - } - return NS_OK; -} - - - -NS_IMETHODIMP -nsTextFrame::GetOffsets(PRInt32 &start, PRInt32 &end) const -{ - start = mContentOffset; - end = mContentOffset+mContentLength; - return NS_OK; -} - -#define TEXT_MAX_NUM_SEGMENTS 65 - -struct SegmentData { - PRUint32 mIsWhitespace : 1; - PRUint32 mContentLen : 31; // content length - - PRBool IsWhitespace() {return PRBool(mIsWhitespace);} - - // Get the content length. This is a running total of all - // the previous segments as well - PRInt32 ContentLen() {return PRInt32(mContentLen);} -}; - -struct TextRun { - // Total number of characters and the accumulated content length - PRInt32 mTotalNumChars, mTotalContentLen; - - // Words and whitespace each count as a segment - PRInt32 mNumSegments; - - // Possible break points specified as offsets into the buffer - PRInt32 mBreaks[TEXT_MAX_NUM_SEGMENTS]; - - // Per segment data - SegmentData mSegments[TEXT_MAX_NUM_SEGMENTS]; - - TextRun() - { - Reset(); - } - - void Reset() - { - mNumSegments = 0; - mTotalNumChars = 0; - mTotalContentLen = 0; - } - - // Returns PR_TRUE if we're currently buffering text - PRBool IsBuffering() - { - return mNumSegments > 0; - } - - void AddSegment(PRInt32 aNumChars, PRInt32 aContentLen, PRBool aIsWhitespace) - { - NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow"); -#ifdef IBMBIDI - if (mNumSegments >= TEXT_MAX_NUM_SEGMENTS) { - return; - } -#endif // IBMBIDI - mTotalNumChars += aNumChars; - mBreaks[mNumSegments] = mTotalNumChars; - mSegments[mNumSegments].mIsWhitespace = aIsWhitespace; - mTotalContentLen += aContentLen; - mSegments[mNumSegments].mContentLen = PRUint32(mTotalContentLen); - mNumSegments++; - } -}; - -PRUint32 -nsTextFrame::EstimateNumChars(PRUint32 aAvailableWidth, - PRUint32 aAverageCharWidth) -{ - // Estimate the number of characters that will fit. Use 105% of the available - // width divided by the average character width. - // If mAveCharWidth is zero, we can fit the entire line. - if (aAverageCharWidth == 0) { - return PR_UINT32_MAX; - } - - PRUint32 estimatedNumChars = aAvailableWidth / aAverageCharWidth; - return estimatedNumChars + estimatedNumChars / 20; -} - -// Replaced by precompiled CCMap (see bug 180266). To update the list -// of characters, see one of files included below. As for the way -// the original list of characters was obtained by Frank Tang, see bug 54467. -// Updated to fix the regression (bug 263411). The list contains -// characters of the following Unicode character classes : Ps, Pi, Po, Pf, Pe. -// (ref.: http://www.w3.org/TR/2004/CR-CSS21-20040225/selector.html#first-letter) -// Note that the file does NOT yet include non-BMP characters because -// there's no point including them without fixing the way we identify -// 'first-letter' currently working only with BMP characters. -#include "punct_marks.ccmap" -DEFINE_CCMAP(gPuncCharsCCMap, const); - -#define IsPunctuationMark(ch) (CCMAP_HAS_CHAR(gPuncCharsCCMap, ch)) - -static PRBool CanBreakBetween(nsTextFrame* aBefore, - PRBool aBreakWhitespaceBefore, - nsTextFrame* aAfter, - PRBool aBreakWhitespaceAfter, - PRBool aSkipLeadingWhitespaceAfter, - nsIFrame* aLineContainer) -{ - // This assumes text transforms don't change text breaking properties - const nsTextFragment* fragBefore = aBefore->GetContent()->GetText(); - const nsTextFragment* fragAfter = aAfter->GetContent()->GetText(); - NS_ASSERTION(fragBefore && fragAfter, "text frames with no text!"); - - // The end of the before-content - PRInt32 beforeOffset = aBefore->GetContentOffset() + aBefore->GetContentLength(); - // The start of the after-content - PRInt32 afterOffset = aAfter->GetContentOffset(); - PRInt32 afterLength = fragAfter->GetLength() - afterOffset; - - if (beforeOffset <= 0 || afterLength <= 0) { - // This shouldn't really happen, text frames shouldn't map no content -#if 0 - NS_WARNING("Textframe maps no content"); -#endif - return PR_FALSE; - } - - PRUnichar lastBefore = fragBefore->CharAt(beforeOffset - 1); - PRUnichar firstAfter = fragAfter->CharAt(afterOffset); - - // If we're skipping leading whitespace in the after-frame, and we actually - // have leading whitespace in the after-frame, then we can't break before - // it. We will rely on a saved break opportunity from the end of the last frame - // (if any). The problem is that we can't accurately figure out here whether - // a break is allowed. - if (aSkipLeadingWhitespaceAfter && XP_IS_SPACE(firstAfter)) - return PR_FALSE; - - while (IS_DISCARDED(firstAfter)) { - ++afterOffset; - --afterLength; - if (afterLength == 0) { - // aAfter will be entirely skipped. No breaking allowed here. - return PR_FALSE; - } - firstAfter = fragAfter->CharAt(afterOffset); - } - while (IS_DISCARDED(lastBefore)) { - --beforeOffset; - if (beforeOffset == 0) { - // aBefore was entirely skipped. Who knows why it called SetTrailingTextFrame. - NS_WARNING("Before-frame should not have called SetTrailingTextFrame"); - return PR_FALSE; - } - lastBefore = fragBefore->CharAt(beforeOffset - 1); - } - - // If the character before or after the boundary is a breakable whitespace - // character that's not skipped, we're good to break. - if ((XP_IS_SPACE(lastBefore) && aBreakWhitespaceBefore) || - (XP_IS_SPACE(firstAfter) && aBreakWhitespaceAfter)) - return PR_TRUE; - // See nsJISx4051LineBreaker::BreakInBetween ... characters 0-255 don't - // trigger complex line breaking behaviour. This is an approximation since - // currently nsJISx4051LineBreaker can look far along a line, but it'll do - // until the line breaker is sorted out. - if (!fragBefore->Is2b() && !fragAfter->Is2b()) - return PR_FALSE; - - nsIFrame* f = - nsLayoutUtils::GetClosestCommonAncestorViaPlaceholders(aBefore, aAfter, aLineContainer); - NS_ASSERTION(f, "Frames to check not in the same document???"); - // Check if our nearest common ancestor allows wrapping between its children - if (f->GetStyleText()->WhiteSpaceCanWrap()) - return PR_FALSE; - - nsAutoString beforeStr; - nsAutoString afterStr; - fragBefore->AppendTo(beforeStr, 0, beforeOffset); - fragAfter->AppendTo(afterStr, afterOffset, afterLength); - - return nsContentUtils::LineBreaker()->BreakInBetween( - beforeStr.get(), beforeStr.Length(), afterStr.get(), afterStr.Length()); -} - -nsReflowStatus -nsTextFrame::MeasureText(nsPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsTextTransformer& aTx, - nsTextStyle& aTs, - TextReflowData& aTextData) -{ - PRBool firstThing = PR_TRUE; - nscoord maxWidth = aReflowState.availableWidth; - nsLineLayout& lineLayout = *aReflowState.mLineLayout; - PRInt32 contentLength = aTx.GetContentLength(); - PRInt32 startingOffset = aTextData.mOffset; - PRInt32 column = mColumn; - nscoord prevAscent = 0, prevDescent = 0; - PRInt32 lastWordLen = 0; - PRUnichar* lastWordPtr = nsnull; - PRBool endsInWhitespace = PR_FALSE; - PRBool endsInNewline = PR_FALSE; - PRBool justDidFirstLetter = PR_FALSE; - nsTextDimensions dimensions, lastWordDimensions; - PRBool measureTextRuns = PR_FALSE; - - // Check whether we can break between the last text frame (if any) and this one - PRBool trailingTextFrameCanWrap; - nsIFrame* lastTextFrame = lineLayout.GetTrailingTextFrame(&trailingTextFrameCanWrap); - PRBool canBreakBetweenTextFrames = PR_FALSE; - if (lastTextFrame) { - NS_ASSERTION(lastTextFrame->GetType() == nsGkAtoms::textFrame, - "Trailing text frame isn't text!"); - canBreakBetweenTextFrames = - CanBreakBetween(NS_STATIC_CAST(nsTextFrame*, lastTextFrame), - trailingTextFrameCanWrap, - this, aTextData.mWrapping, aTextData.mSkipWhitespace, - lineLayout.GetLineContainerFrame()); - } - - if (contentLength == 0) { - aTextData.mX = 0; - aTextData.mAscent = 0; - aTextData.mDescent = 0; - return NS_FRAME_COMPLETE; - } -#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) - // see if we have implementation for GetTextDimensions() - PRUint32 hints = 0; - aReflowState.rendContext->GetHints(hints); - if (hints & NS_RENDERING_HINT_FAST_MEASURE) { - measureTextRuns = !aTs.mPreformatted && - !aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing && - aTextData.mWrapping; - } - // Don't measure text runs with letter spacing active, it doesn't work - // it also doesn't work if we are not word-wrapping (bug 42832) -#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)*/ - TextRun textRun; - PRUint32 estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX, - aTs.mAveCharWidth); - -#ifdef IBMBIDI - nsTextFrame* nextBidi = nsnull; - PRInt32 start = -1, end; - - if (mState & NS_FRAME_IS_BIDI) { - nextBidi = NS_STATIC_CAST(nsTextFrame*, GetLastInFlow()->GetNextContinuation()); - if (nextBidi) { - if (mContentLength < 1) { - mContentLength = 1; - } - nextBidi->GetOffsets(start, end); - if (start <= mContentOffset) { - nextBidi->AdjustOffsetsForBidi(mContentOffset + mContentLength, end); - } - else { - mContentLength = start - mContentOffset; - } - } - } -#endif //IBMBIDI - - aTextData.mX = 0; - if (aTextData.mMeasureText) { - aTs.mNormalFont->GetMaxAscent(aTextData.mAscent); - aTs.mNormalFont->GetMaxDescent(aTextData.mDescent); - } - PRBool firstWordDone = PR_FALSE; - for (;;) { -#ifdef IBMBIDI - if (nextBidi && (mContentLength <= 0) ) { - if (textRun.IsBuffering()) { - // Measure the remaining text - goto MeasureTextRun; - } - else { - break; - } - } -#endif // IBMBIDI - // Get next word/whitespace from the text - PRBool isWhitespace, wasTransformed; - PRInt32 wordLen, contentLen; - union { - char* bp1; - PRUnichar* bp2; - }; -#ifdef IBMBIDI - wordLen = start; -#endif // IBMBIDI - - bp2 = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace, - &wasTransformed, textRun.mNumSegments == 0); - - // We need to set aTextData.mCanBreakBefore to true after 1st word. But we can't set - // aTextData.mCanBreakBefore without seeing the 2nd word. That's because this frame - // may only contain part of one word, the other part is in next frame. - // we don't care if first word is whitespace, that will be addressed later. - if (!aTextData.mCanBreakBefore && !firstThing && !isWhitespace) { - firstWordDone = PR_TRUE; - } - -#ifdef IBMBIDI - if (nextBidi) { - mContentLength -= contentLen; - - if (mContentLength < 0) { - contentLen += mContentLength; - wordLen = PR_MIN(wordLen, contentLen); - } - } -#endif // IBMBIDI - // Remember if the text was transformed - if (wasTransformed) { - mState |= TEXT_WAS_TRANSFORMED; - } - - if (bp2) { - if (firstWordDone) { - // The first word has been processed, and 2nd word is seen - // we can set it be breakable here after. - aTextData.mCanBreakBefore = PR_TRUE; - } - } else { - if (textRun.IsBuffering()) { - // Measure the remaining text - goto MeasureTextRun; - } - else { - // Advance the offset in case we just consumed a bunch of - // discarded characters. Otherwise, if this is the first piece - // of content for this frame we will attempt to break-before it. - aTextData.mOffset += contentLen; - break; - } - } - - lastWordLen = wordLen; - lastWordPtr = bp2; - aTextData.mInWord = PR_FALSE; - - // Measure the word/whitespace - PRUnichar firstChar; - if (aTx.TransformedTextIsAscii()) { - firstChar = *bp1; - } else { - firstChar = *bp2; - } - if (isWhitespace) { - if ('\n' == firstChar) { - // We hit a newline. Stop looping. - NS_ASSERTION(aTs.mPreformatted, "newline w/o ts.mPreformatted"); - aTextData.mOffset++; - endsInWhitespace = PR_TRUE; - endsInNewline = PR_TRUE; - break; - } - if (aTextData.mSkipWhitespace) { - aTextData.mOffset += contentLen; - aTextData.mSkipWhitespace = PR_FALSE; // XXXldb Eh? - - if (wasTransformed) { - // As long as there were no discarded characters, then don't consider - // skipped leading whitespace as being transformed - if (wordLen == contentLen) { - mState &= ~TEXT_WAS_TRANSFORMED; - } - } - - // Only set flag when we actually do skip whitespace - mState |= TEXT_SKIP_LEADING_WS; - continue; - } - firstThing = PR_FALSE; - - // NOTE: Even if the textRun absorbs the whitespace below, we still - // want to remember that we're breakable. - aTextData.mCanBreakBefore = PR_TRUE; - aTextData.mFirstLetterOK = PR_FALSE; - - if ('\t' == firstChar) { - // Expand tabs to the proper width - wordLen = 8 - (7 & column); - // Apply word spacing to every space derived from a tab - dimensions.width = (aTs.mSpaceWidth + aTs.mWordSpacing + aTs.mLetterSpacing)*wordLen; - - // Because we have to expand the tab when rendering consider that - // a transformation of the text - mState |= TEXT_WAS_TRANSFORMED; - } - else if (textRun.IsBuffering()) { - // Add a whitespace segment - textRun.AddSegment(wordLen, contentLen, PR_TRUE); - continue; - } - else { - // Apply word spacing to every space, if there's more than one - dimensions.width = wordLen*(aTs.mWordSpacing + aTs.mLetterSpacing + aTs.mSpaceWidth);// XXX simplistic - } - - //Even if there is not enough space for this "space", we still put it - //here instead of next line - column += wordLen; - endsInWhitespace = PR_TRUE; - aTextData.mOffset += contentLen; - - if (aTextData.mMeasureText) { - //if we're wrapping, then don't add the whitespace width to the - // x-offset unless the whitespace will fit within maxWidth.'' - if (aTextData.mWrapping) { - if (aTextData.mX + dimensions.width <= maxWidth) { - aTextData.mX += dimensions.width; - } - else { - // since we didn't add the trailing space width, set this flag so that - // we will not trim this non-existing space - aTextData.mTrailingSpaceTrimmed = PR_TRUE; - // Note: word-spacing or letter-spacing can make the "space" really - // wide. But since this space is left out from our width, linelayout - // may still try to fit something narrower at the end of the line. - // So on return (see below), we flag a soft-break status to ensure - // that linelayout doesn't place something where the "space" should - // be. - break; - } - } - else { - //if we're not wrapping, then always advance - // the x-offset regardless of maxWidth - aTextData.mX += dimensions.width; - } - } //(aTextData.mMeasureText) - } - else { - firstThing = PR_FALSE; - - aTextData.mSkipWhitespace = PR_FALSE; - - // XXX :first-letter should be handled during frame construction - // (and it has a good bit in common with nextBidi) - if (aTextData.mFirstLetterOK) { - if (IsPunctuationMark(firstChar)) { - if (contentLen > 1) - { - wordLen = 2; - contentLen = 2; - } - } - else { - wordLen = 1; - contentLen = 1; - } - justDidFirstLetter = PR_TRUE; - } - - if (aTextData.mMeasureText) { - if (measureTextRuns && !justDidFirstLetter) { - // Add another word to the text run - textRun.AddSegment(wordLen, contentLen, PR_FALSE); - - // See if we should measure the text - if ((textRun.mTotalNumChars >= estimatedNumChars) || - (textRun.mNumSegments >= (TEXT_MAX_NUM_SEGMENTS - 1))) { - goto MeasureTextRun; - } - } - else { - if (aTs.mSmallCaps) { - MeasureSmallCapsText(aReflowState.rendContext, aTs, bp2, wordLen, PR_FALSE, &dimensions); - } - else { - // Measure just the one word - if (aTx.TransformedTextIsAscii()) { - aReflowState.rendContext->GetTextDimensions(bp1, wordLen, dimensions); - } else { - aReflowState.rendContext->GetTextDimensions(bp2, wordLen, dimensions); - } -#ifdef MOZ_MATHML - // If GetBoundingMetrics is available, use the exact glyph metrics - // for ::first-letter - // XXX remove the #ifdef if GetBoundingMetrics becomes mainstream - if (justDidFirstLetter) { - nsresult res; - nsBoundingMetrics bm; - if (aTx.TransformedTextIsAscii()) { - res = aReflowState.rendContext->GetBoundingMetrics(bp1, wordLen, bm); - } else { - res = aReflowState.rendContext->GetBoundingMetrics(bp2, wordLen, bm); - } - if (NS_SUCCEEDED(res)) { - aTextData.mAscent = dimensions.ascent = bm.ascent; - aTextData.mDescent = dimensions.descent = bm.descent; - } - } -#endif - if (aTs.mLetterSpacing) { - dimensions.width += aTs.mLetterSpacing * wordLen; - } - - if (aTs.mWordSpacing) { - if (aTx.TransformedTextIsAscii()) { - for (char* bp = bp1; bp < bp1 + wordLen; bp++) { - if (*bp == ' ') // || *bp == CH_CJKSP) - dimensions.width += aTs.mWordSpacing; - } - } else { - for (PRUnichar* bp = bp2; bp < bp2 + wordLen; bp++) { - if (*bp == ' ') // || *bp == CH_CJKSP) - dimensions.width += aTs.mWordSpacing; - } - } - } - } - - lastWordDimensions = dimensions; - - PRBool canBreak; - if (0 == aTextData.mX) { - canBreak = canBreakBetweenTextFrames; - // Allow breaking between text frames even if mWrapping is false - // (e.g., we're white-space:pre). If canBreakBetweenTextFrames is - // true, then the previous text frame's mWrapping must have been - // true, and we allow breaking between text frames if at least - // one of them allows breaking. - } else { - canBreak = aTextData.mWrapping; - } - if (canBreak) { - // Remember that we *could* have broken here, even if we choose not to - PRBool forceBreak = - lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset, PR_TRUE); - // See if there is room for the text - if (forceBreak || (aTextData.mX + dimensions.width > maxWidth)) { - // The text will not fit, or a break was forced. - break; - } - } - prevAscent = aTextData.mAscent; - prevDescent = aTextData.mDescent; - - aTextData.mX += dimensions.width; - if (aTextData.mAscent < dimensions.ascent) { - aTextData.mAscent = dimensions.ascent; - } - if (aTextData.mDescent < dimensions.descent) { - aTextData.mDescent = dimensions.descent; - } - - column += wordLen; - endsInWhitespace = PR_FALSE; - aTextData.mOffset += contentLen; - if (justDidFirstLetter) { - // Time to stop - break; - } - } - } - else { - // Remember that we *could* have broken before this chunk of text. - PRBool canBreak; - if (aTextData.mOffset == startingOffset) { - canBreak = canBreakBetweenTextFrames; - } else { - canBreak = aTextData.mWrapping; - } - if (canBreak) { -#ifdef DEBUG - PRBool forceBreak = -#endif - lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset, PR_TRUE); - NS_ASSERTION(!forceBreak, "If we're supposed to break, we should be " - "really measuring"); - } - - // We didn't measure the text, but we need to update our state - column += wordLen; - endsInWhitespace = PR_FALSE; - aTextData.mOffset += contentLen; - if (justDidFirstLetter) { - // Time to stop - break; - } - } - } - continue; - - MeasureTextRun: -#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) - // see if we have implementation for GetTextDimensions() - if (hints & NS_RENDERING_HINT_FAST_MEASURE) { - PRInt32 numCharsFit; - - PRInt32 forcedOffset = lineLayout.GetForcedBreakPosition(GetContent()); - PRInt32 measureChars = textRun.mTotalNumChars; - if (forcedOffset == -1) { - forcedOffset -= aTextData.mOffset; -#ifdef MOZ_CAIRO_GFX - NS_ASSERTION(forcedOffset >= 0, - "Overshot forced offset, we should have already exited"); -#endif - if (forcedOffset >= 0 && forcedOffset < textRun.mTotalNumChars) { - // Only measure up to forcedOffset characters. We still need to measure - // to make sure we get the right text dimensions, even though we know - // where we're going to break. This will reduce the number of chars that - // fit, which we then detect as a required break. - measureChars = forcedOffset; - } - } - - // These calls can return numCharsFit not positioned at a break in the textRun. Beware. - if (aTx.TransformedTextIsAscii()) { - aReflowState.rendContext->GetTextDimensions((char*)aTx.GetWordBuffer(), measureChars, - maxWidth - aTextData.mX, - textRun.mBreaks, textRun.mNumSegments, - dimensions, numCharsFit, lastWordDimensions); - } else { - aReflowState.rendContext->GetTextDimensions(aTx.GetWordBuffer(), measureChars, - maxWidth - aTextData.mX, - textRun.mBreaks, textRun.mNumSegments, - dimensions, numCharsFit, lastWordDimensions); - } - - // See how much of the text fit - PRBool canBreak; - if (0 == aTextData.mX) { - canBreak = canBreakBetweenTextFrames; - } else { - canBreak = aTextData.mWrapping; - } - if (canBreak && aTextData.mX + dimensions.width > maxWidth) { - // None of the text fits -#ifdef IBMBIDI - nextBidi = nsnull; -#endif // IBMBIDI - break; - } - - // Find the index of the last segment that fit - PRInt32 lastSegment; - if (numCharsFit >= textRun.mTotalNumChars) { // fast path, normal case - NS_ASSERTION(numCharsFit == textRun.mTotalNumChars, "shouldn't overshoot"); - lastSegment = textRun.mNumSegments - 1; - } else { - for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ; - NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment"); - // now we have textRun.mBreaks[lastSegment] >= numCharsFit - /* O'Callahan XXX: This snippet together with the snippet below prevents mail from loading - Justification seems to work just fine without these changes. - We get into trouble in a case where lastSegment gets set to -1 - - if (textRun.mBreaks[lastSegment] > numCharsFit) { - // NOTE: this segment did not actually fit! - lastSegment--; - } - */ - } - - /* O'Callahan XXX: This snippet together with the snippet above prevents mail from loading - - if (lastSegment < 0) { - // no segments fit - break; - } */ - - aTextData.mX += dimensions.width; - if (aTextData.mAscent < dimensions.ascent) { - aTextData.mAscent = dimensions.ascent; - } - if (aTextData.mDescent < dimensions.descent) { - aTextData.mDescent = dimensions.descent; - } - // this is where to backup if line-breaking happens to push the last word - prevAscent = aTextData.mAscent; - prevDescent = aTextData.mDescent; - // we can now consider the last word since we know where to backup - if (aTextData.mAscent < lastWordDimensions.ascent) { - aTextData.mAscent = lastWordDimensions.ascent; - } - if (aTextData.mDescent < lastWordDimensions.descent) { - aTextData.mDescent = lastWordDimensions.descent; - } - - endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace(); - - // Save last possible backup position. Whitespace segments are always - // breakable since nsTextTransformer reports preformatted spaces as - // "not whitespace". Note that each segment is either entirely whitespace - // or entirely non-whitespace. - PRInt32 lastWhitespaceSegment = - endsInWhitespace ? lastSegment : lastSegment - 1; - if (lastWhitespaceSegment >= 0) { - lineLayout.NotifyOptionalBreakPosition(GetContent(), - aTextData.mOffset + textRun.mSegments[lastWhitespaceSegment].ContentLen(), - PR_TRUE); - } - - column += numCharsFit; - aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen(); - - // If all the text didn't fit, then we're done - if (numCharsFit != textRun.mTotalNumChars) { -#ifdef IBMBIDI - nextBidi = nsnull; -#endif // IBMBIDI - break; - } - -#ifdef IBMBIDI - if (nextBidi && (mContentLength <= 0) ) { - break; - } -#endif // IBMBIDI - - if (nsnull == bp2) { - // No more text so we're all finished. Advance the offset in case the last - // call to GetNextWord() discarded characters - aTextData.mOffset += contentLen; - break; - } - - // Reset the number of text run segments - textRun.Reset(); - - // Estimate the remaining number of characters we think will fit - estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX, - aTs.mAveCharWidth); - } -#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */ - ; - } - - // If we didn't actually measure any text, then make sure it looks - // like we did - if (!aTextData.mMeasureText) { - aTextData.mAscent = mAscent; - aTextData.mDescent = mRect.height - aTextData.mAscent; - aTextData.mX = mRect.width; - if (mState & TEXT_TRIMMED_WS) { - // Add back in the width of a space since it was trimmed away last time - // NOTE: Trailing whitespace includes word and letter spacing! - aTextData.mX += aTs.mSpaceWidth + aTs.mWordSpacing + aTs.mLetterSpacing; - } - } - - // Inform line layout of how this piece of text ends in whitespace - // (only text objects do this). Note that if x is zero then this - // text object collapsed into nothingness which means it shouldn't - // effect the current setting of the ends-in-whitespace flag, nor should it - // be setting InWord state, and it should be ignored when subsequent text - // considers whether it can break-before. - lineLayout.SetColumn(column); - lineLayout.SetUnderstandsWhiteSpace(PR_TRUE); - if (0 != aTextData.mX) { - lineLayout.SetTrailingTextFrame(this, aTextData.mWrapping); - lineLayout.SetEndsInWhiteSpace(endsInWhitespace); - lineLayout.SetInWord(!endsInWhitespace); - } else { - // Don't allow subsequent text frame to break-before. All our text is - // being skipped (usually whitespace, could be discarded Unicode control - // characters). - lineLayout.SetTrailingTextFrame(nsnull, PR_FALSE); - lineLayout.SetInWord(PR_FALSE); - } - if (justDidFirstLetter) { - lineLayout.SetFirstLetterFrame(this); - lineLayout.SetFirstLetterStyleOK(PR_FALSE); - mState |= TEXT_FIRST_LETTER; - } - - // Return our reflow status - nsReflowStatus rs = (aTextData.mOffset == contentLength) -#ifdef IBMBIDI - || (aTextData.mOffset == start) -#endif // IBMBIDI - ? NS_FRAME_COMPLETE - : NS_FRAME_NOT_COMPLETE; - - if (canBreakBetweenTextFrames && aTextData.mOffset == startingOffset) { - // Couldn't place any text but we can break between text frames, so do that. - return NS_INLINE_LINE_BREAK_BEFORE(); - } - - if (endsInNewline) { - lineLayout.SetLineEndsInBR(PR_TRUE); - return NS_INLINE_LINE_BREAK_AFTER(rs); - } - - if (aTextData.mTrailingSpaceTrimmed && rs == NS_FRAME_COMPLETE) { - // Flag a soft-break that we can check (below) if we come back here - lineLayout.SetLineEndsInSoftBR(PR_TRUE); - } else if (lineLayout.GetLineEndsInSoftBR() && !lineLayout.GetEndsInWhiteSpace()) { - // Break-before a word that follows the soft-break flagged earlier - return NS_INLINE_LINE_BREAK_BEFORE(); - } - - if (rs == NS_FRAME_COMPLETE && 0 != aTextData.mX && endsInWhitespace && - aTextData.mWrapping) { - // Remember the break opportunity at the end of this frame - if (lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset, - aTextData.mX <= maxWidth)) - return NS_INLINE_LINE_BREAK_AFTER(rs); - } - - if ((aTextData.mOffset != contentLength) && (aTextData.mOffset == startingOffset)) { - // Break-before a long-word that doesn't fit here - return NS_INLINE_LINE_BREAK_BEFORE(); - } - - return rs; -} - -/* virtual */ void -nsTextFrame::MarkIntrinsicWidthsDirty() -{ - // Clear the TEXT_OPTIMIZE_RESIZE for the next time around. It'll get - // reset late in Reflow. - RemoveStateBits(TEXT_OPTIMIZE_RESIZE); - - nsFrame::MarkIntrinsicWidthsDirty(); -} - -// XXX This should really share more code with the first half of MeasureText. -/* virtual */ void -nsTextFrame::AddInlineMinWidth(nsIRenderingContext *aRenderingContext, - nsIFrame::InlineMinWidthData *aData) -{ - nsresult rv; - - nsPresContext *presContext = PresContext(); - nsTextStyle ts(presContext, *aRenderingContext, mStyleContext); - SetupTextRunDirection(presContext, aRenderingContext); - if (!ts.mFont->mSize) - // XXX If font size is zero, we still need to figure out whether we've - // got non-whitespace text and whether we end in whitespace. - return; - - const nsStyleText *styleText = GetStyleText(); - PRBool wrapping = styleText->WhiteSpaceCanWrap(); - PRBool wsSignificant = styleText->WhiteSpaceIsSignificant(); - PRBool atStart = PR_TRUE; - PRBool forceArabicShaping = (ts.mSmallCaps || - (0 != ts.mWordSpacing) || - (0 != ts.mLetterSpacing) || - ts.mJustifying); - nsTextTransformer tx(presContext); - // Keep the text in ascii if possible. Note that if we're measuring small - // caps text then transform to Unicode because the helper function only - // accepts Unicode text - rv = tx.Init(this, mContent, mContentOffset, forceArabicShaping, - !ts.mSmallCaps); - if (NS_FAILED(rv)) { - NS_NOTREACHED("failure initializing text transformer"); - return; - } - - if (aData->trailingTextFrame && - CanBreakBetween(NS_STATIC_CAST(nsTextFrame*, aData->trailingTextFrame), - aData->trailingTextFrame-> - GetStyleText()->WhiteSpaceCanWrap(), - this, wrapping, - aData->skipWhitespace, // XXX ??? - nsnull)) // XXX Better to pass real frame - { - aData->OptionallyBreak(aRenderingContext); - } - - for (;;) { - union { - char* bp1; - PRUnichar* bp2; - }; - PRInt32 wordLen = -1, contentLen; - PRBool isWhitespace, wasTransformed; - // XXX Is !aData->skipWhitespace the right criterion for when the - // text transformer should capitalize the first letter? - bp2 = tx.GetNextWord(!aData->skipWhitespace, &wordLen, &contentLen, - &isWhitespace, &wasTransformed); - if (!bp2) - break; - // XXX Watch mContentLength! - - if (isWhitespace) { - PRUnichar firstChar; - if (tx.TransformedTextIsAscii()) { - firstChar = *bp1; - } else { - firstChar = *bp2; - } - if ('\n' == firstChar) { - aData->ForceBreak(aRenderingContext); - aData->skipWhitespace = PR_TRUE; - aData->trailingWhitespace = 0; - } else if (!aData->skipWhitespace || wsSignificant) { - atStart = PR_FALSE; - nscoord width; - if ('\t' == firstChar) { - // XXX Need to track column! - wordLen = 8; - // Apply word spacing to every space derived from a tab - width = - (ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing)*wordLen; - } else { - // Apply word spacing to every space, if there's more than one - width = - wordLen*(ts.mWordSpacing + ts.mLetterSpacing + ts.mSpaceWidth);// XXX simplistic - } - aData->currentLine += width; - aData->atStartOfLine = PR_FALSE; - if (wsSignificant) { - aData->trailingWhitespace = 0; - aData->skipWhitespace = PR_FALSE; - } else { - aData->trailingWhitespace += width; - aData->skipWhitespace = PR_TRUE; - } - - if (wrapping) { - aData->OptionallyBreak(aRenderingContext); - } - } - } else { - if (!atStart && wrapping) { - aData->OptionallyBreak(aRenderingContext); - } - - atStart = PR_FALSE; - - nscoord width; - if (ts.mSmallCaps) { - nsTextDimensions dimensions; - // MeasureSmallCapsText measures one character at a time so it *should* - // be OK to just say "LTR" here without breaking things (any more than - // they're already broken) - aRenderingContext->SetTextRunRTL(PR_FALSE); - MeasureSmallCapsText(aRenderingContext, ts, bp2, wordLen, PR_FALSE, - &dimensions); - width = dimensions.width; - } else { - // Unfortunately we might have mixed-directionality text at this point, - // and we might not know our embedding level. So we have to make some - // approximations, until bidi resolution is moved to frame construction. - if (tx.TransformedTextIsAscii()) { - // It may not actually be LTR, but hopefully the width doesn't care - aRenderingContext->SetTextRunRTL(PR_FALSE); - aRenderingContext->GetWidth(bp1, wordLen, width); - } else { - // We may get the directions reversed but at least we'll be breaking - // the string up and measuring segments in the same direction - width = - nsLayoutUtils::GetStringWidth(this, aRenderingContext, bp2, wordLen); - } - width += ts.mLetterSpacing * wordLen; - } - - aData->currentLine += width; - aData->atStartOfLine = PR_FALSE; - aData->skipWhitespace = PR_FALSE; - aData->trailingWhitespace = 0; - } - } - - aData->trailingTextFrame = this; -} - -/* virtual */ void -nsTextFrame::AddInlinePrefWidth(nsIRenderingContext *aRenderingContext, - nsIFrame::InlinePrefWidthData *aData) -{ - nsresult rv; - - nsPresContext *presContext = PresContext(); - nsTextStyle ts(presContext, *aRenderingContext, mStyleContext); - if (!ts.mFont->mSize) - // XXX If font size is zero, we still need to figure out whether we've - // got non-whitespace text and whether we end in whitespace. - return; - - PRBool forceArabicShaping = (ts.mSmallCaps || - (0 != ts.mWordSpacing) || - (0 != ts.mLetterSpacing) || - ts.mJustifying); - nsTextTransformer tx(presContext); - // Keep the text in ascii if possible. Note that if we're measuring small - // caps text then transform to Unicode because the helper function only - // accepts Unicode text - rv = tx.Init(this, mContent, mContentOffset, forceArabicShaping, - !ts.mSmallCaps); - if (NS_FAILED(rv)) { - NS_NOTREACHED("failure initializing text transformer"); - return; - } - - for (;;) { - union { - char* bp1; - PRUnichar* bp2; - }; - PRInt32 wordLen = -1, contentLen; - PRBool isWhitespace, wasTransformed; - // XXX Should fix this to use something better than GetNextWord! - // XXX Is !aData->skipWhitespace the right criterion for when the - // text transformer should capitalize the first letter? - bp2 = tx.GetNextWord(!aData->skipWhitespace, &wordLen, &contentLen, - &isWhitespace, &wasTransformed); - if (!bp2) - break; - // XXX Watch mContentLength! - - if (isWhitespace) { - PRUnichar firstChar; - if (tx.TransformedTextIsAscii()) { - firstChar = *bp1; - } else { - firstChar = *bp2; - } - if ('\n' == firstChar) { - aData->ForceBreak(aRenderingContext); - } else if (!aData->skipWhitespace) { - nscoord width; - if ('\t' == firstChar) { - // XXX Need to track column! - wordLen = 8; - // Apply word spacing to every space derived from a tab - width = - (ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing)*wordLen; - } else { - // Apply word spacing to every space, if there's more than one - width = - wordLen*(ts.mWordSpacing + ts.mLetterSpacing + ts.mSpaceWidth);// XXX simplistic - } - aData->currentLine += width; - if (GetStyleText()->WhiteSpaceIsSignificant()) - // XXX Should we also subtract the old value of - // trailingWhitespace from currentLine? - aData->trailingWhitespace = 0; - else - aData->trailingWhitespace += width; - } - } else { - nscoord width; - if (ts.mSmallCaps) { - nsTextDimensions dimensions; - // MeasureSmallCapsText measures one character at a time so it *should* - // be OK to just say "LTR" here without breaking things (any more than - // they're already broken) - aRenderingContext->SetTextRunRTL(PR_FALSE); - MeasureSmallCapsText(aRenderingContext, ts, bp2, wordLen, PR_FALSE, - &dimensions); - width = dimensions.width; - } else { - // Unfortunately we might have mixed-directionality text at this point, - // because we may not have reflowed yet, and we might not know our - // embedding level. So we have to make some approximations, until - // bidi resolution is moved to frame construction. - if (tx.TransformedTextIsAscii()) { - // It may not actually be LTR, but hopefully the width doesn't care - aRenderingContext->SetTextRunRTL(PR_FALSE); - aRenderingContext->GetWidth(bp1, wordLen, width); - } else { - // We may get the directions reversed but at least we'll be breaking - // the string up and measuring segments in the same direction - width = - nsLayoutUtils::GetStringWidth(this, aRenderingContext, bp2, wordLen); - } - width += ts.mLetterSpacing * wordLen; - } - - aData->currentLine += width; - aData->skipWhitespace = PR_FALSE; - aData->trailingWhitespace = 0; - } - } -} - -/* virtual */ nsSize -nsTextFrame::ComputeSize(nsIRenderingContext *aRenderingContext, - nsSize aCBSize, nscoord aAvailableWidth, - nsSize aMargin, nsSize aBorder, nsSize aPadding, - PRBool aShrinkWrap) -{ - // Inlines and text don't compute size before reflow. - return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); -} - -NS_IMETHODIMP -nsTextFrame::Reflow(nsPresContext* aPresContext, - nsHTMLReflowMetrics& aMetrics, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) -{ - DO_GLOBAL_REFLOW_COUNT("nsTextFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); -#ifdef NOISY_REFLOW - ListTag(stdout); - printf(": BeginReflow: availableSize=%d,%d\n", - aReflowState.availableWidth, aReflowState.availableHeight); -#endif - - mState &= ~TEXT_IS_END_OF_LINE; - - // XXX If there's no line layout, we shouldn't even have created this - // frame. This may happen if, for example, this is text inside a table - // but not inside a cell. For now, just don't reflow. - if (nsnull == aReflowState.mLineLayout) { - // XXX Add a method to aMetrics that does this; we do it several places - aMetrics.width = 0; - aMetrics.height = 0; - aMetrics.ascent = 0; -#ifdef MOZ_MATHML - if (NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags) - aMetrics.mBoundingMetrics.Clear(); -#endif - return NS_OK; - } - - // Get starting offset into the content - PRInt32 startingOffset = 0; - nsIFrame* prevInFlow = GetPrevInFlow(); - if (nsnull != prevInFlow) { - nsTextFrame* prev = NS_STATIC_CAST(nsTextFrame*, prevInFlow); - startingOffset = prev->mContentOffset + prev->mContentLength; - - // If our starting offset doesn't agree with mContentOffset, then our - // prev-in-flow has changed the number of characters it maps and so we - // need to measure text and not try and optimize a resize reflow - if (startingOffset != mContentOffset) { - mState &= ~TEXT_OPTIMIZE_RESIZE; - } - } - nsLineLayout& lineLayout = *aReflowState.mLineLayout; - nsTextStyle ts(aPresContext, *aReflowState.rendContext, mStyleContext); - SetupTextRunDirection(aPresContext, aReflowState.rendContext); - - if ( (mContentLength > 0) && (mState & NS_FRAME_IS_BIDI) ) { - startingOffset = mContentOffset; - } - - if (aPresContext->BidiEnabled()) { - nsCharType charType = eCharType_LeftToRight; - PRUint32 hints = 0; - aReflowState.rendContext->GetHints(hints); - charType = (nsCharType)NS_PTR_TO_INT32(aPresContext->PropertyTable()->GetProperty(this, nsGkAtoms::charType)); - if ((eCharType_RightToLeftArabic == charType && - (hints & NS_RENDERING_HINT_ARABIC_SHAPING) == NS_RENDERING_HINT_ARABIC_SHAPING) || - (eCharType_RightToLeft == charType && - (hints & NS_RENDERING_HINT_BIDI_REORDERING) == NS_RENDERING_HINT_BIDI_REORDERING)) { - // XXXldb This needs to happen before |Reflow|. - aPresContext->SetIsBidiSystem(PR_TRUE); - } - } - - // Clear out the reflow state flags in mState (without destroying - // the TEXT_BLINK_ON bit). - PRBool lastTimeWeSkippedLeadingWS = 0 != (mState & TEXT_SKIP_LEADING_WS); - mState &= ~TEXT_REFLOW_FLAGS; - if (aReflowState.mFlags.mBlinks) { - if (0 == (mState & TEXT_BLINK_ON)) { - mState |= TEXT_BLINK_ON; - nsBlinkTimer::AddBlinkFrame(aPresContext, this); - } - } - else { - if (0 != (mState & TEXT_BLINK_ON)) { - mState &= ~TEXT_BLINK_ON; - nsBlinkTimer::RemoveBlinkFrame(this); - } - } - - PRBool wrapping = ts.mText->WhiteSpaceCanWrap(); - - // Set whitespace skip flag - PRBool skipWhitespace = PR_FALSE; - if (!ts.mPreformatted) { - if (lineLayout.GetEndsInWhiteSpace()) { - skipWhitespace = PR_TRUE; - } - } - - nscoord maxWidth = aReflowState.availableWidth; - - // Setup text transformer to transform this frames text content - nsIDocument* doc = mContent->GetDocument(); - if (!doc) { - NS_WARNING("Content has no document."); - return NS_ERROR_FAILURE; - } - PRBool forceArabicShaping = (ts.mSmallCaps || - (0 != ts.mWordSpacing) || - (0 != ts.mLetterSpacing) || - ts.mJustifying); - nsTextTransformer tx(aPresContext); - // Keep the text in ascii if possible. Note that if we're measuring small - // caps text then transform to Unicode because the helper function only - // accepts Unicode text - nsresult rv = tx.Init(this, mContent, startingOffset, forceArabicShaping, !ts.mSmallCaps); - if (NS_OK != rv) { - return rv; - } - //PRInt32 contentLength = tx.GetContentLength(); - - // Set inWord to true if we are part of a previous piece of text's word. This - // is only valid for one pass through the measuring loop. - PRBool inWord = lineLayout.InWord(); - if (inWord) { - mState |= TEXT_IN_WORD; - } - mState &= ~TEXT_FIRST_LETTER; - - PRInt32 column = lineLayout.GetColumn(); - PRInt32 prevColumn = mColumn; - mColumn = column; - PRBool measureText = PR_TRUE; - - // We can avoid actually measuring the text if: - // - intrinsic widths haven't been marked dirty (which clears - // TEXT_OPTIMIZE_RESIZE) - // - we don't have a next in flow - // - the previous reflow successfully reflowed all text in the - // available space - // - we aren't computing the max element size (that requires we measure - // text) - // - skipping leading whitespace is the same as it was the last time - // - we're wrapping text and the available width is at least as big as our - // current frame width -or- - // we're not wrapping text and we're at the same column as before (this is - // an issue for preformatted tabbed text only) - // - AND we aren't justified (in which case the frame width has already been tweaked and can't be used) - nscoord realWidth = mRect.width; - if (mState & TEXT_TRIMMED_WS) { - // NOTE: Trailing whitespace includes word and letter spacing! - realWidth += ts.mSpaceWidth + ts.mWordSpacing + ts.mLetterSpacing; - } - if (!GetNextInFlow() && - (mState & TEXT_OPTIMIZE_RESIZE) && - lineLayout.GetForcedBreakPosition(GetContent()) == -1 && - (lastTimeWeSkippedLeadingWS == skipWhitespace) && - ((wrapping && (maxWidth >= realWidth)) || - (!wrapping && (prevColumn == column))) && -#ifdef IBMBIDI - (0 == (mState & NS_FRAME_IS_BIDI) ) && -#endif // IBMBIDI - !ts.mJustifying) { - // We can skip measuring of text and use the value from our - // previous reflow - measureText = PR_FALSE; -#ifdef NOISY_REFLOW - printf(" => measureText=%s wrapping=%s skipWhitespace=%s", - measureText ? "yes" : "no", - wrapping ? "yes" : "no", - skipWhitespace ? "yes" : "no"); - printf(" realWidth=%d maxWidth=%d\n", - realWidth, maxWidth); -#endif - } - - // Local state passed to the routines that do the actual text measurement - TextReflowData textData(startingOffset, wrapping, skipWhitespace, - measureText, inWord, lineLayout.GetFirstLetterStyleOK(), - lineLayout.LineIsBreakable(), PR_FALSE); - - // Measure the text - // MeasureText may set TEXT_TRIMMED_WS flag, so don't clear after the call - if (ts.mFont->mSize) - aStatus = MeasureText(aPresContext, aReflowState, tx, ts, textData); - else { - textData.mX = 0; - textData.mAscent = 0; - textData.mDescent = 0; - aStatus = NS_FRAME_COMPLETE; - } - if (textData.mTrailingSpaceTrimmed) - mState |= TEXT_TRIMMED_WS; - else - mState &= ~TEXT_TRIMMED_WS; - - if (tx.HasMultibyte()) { - mState |= TEXT_HAS_MULTIBYTE; - } - - // Setup metrics for caller; store final max-element-size information - aMetrics.width = textData.mX; - if ((0 == textData.mX) && !ts.mPreformatted) { - aMetrics.height = 0; - aMetrics.ascent = 0; - } - else { - aMetrics.ascent = textData.mAscent; - aMetrics.height = textData.mAscent + textData.mDescent; - } - mAscent = aMetrics.ascent; - - // Set content offset and length - mContentOffset = startingOffset; - mContentLength = textData.mOffset - startingOffset; - - // Compute space and letter counts for justification, if required - // Also use this one-shot path to compute the metrics needed for MathML, if required - // (the flag is set only if this text happens to be inside MathML) - PRBool calcMathMLMetrics = PR_FALSE; - nsAutoTextBuffer* textBufferPtr = nsnull; -#ifdef MOZ_MATHML - nsAutoTextBuffer textBuffer; - calcMathMLMetrics = (NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags) != 0; - if (calcMathMLMetrics) { - textBufferPtr = &textBuffer; - // always use the Unicode path with MathML fonts in gfx, it is safer this way - mState |= TEXT_HAS_MULTIBYTE; - } -#endif - if (ts.mJustifying || calcMathMLMetrics) { - PRIntn numJustifiableCharacter; - PRInt32 textLength; - - // This will include a space for trailing whitespace, if any is present. - // This is corrected for in nsLineLayout::TrimWhiteSpaceIn. - - // This work could be done in MeasureText, but it's complex to do accurately - // there because of the need to repair counts when wrapped words are backed out. - // So I do it via PrepareUnicodeText ... a little slower perhaps, but a lot saner, - // and it localizes the counting logic to one place. - PrepareUnicodeText(tx, nsnull, textBufferPtr, &textLength, PR_TRUE, &numJustifiableCharacter); - lineLayout.SetTextJustificationWeights(numJustifiableCharacter, textLength - numJustifiableCharacter); - -#ifdef MOZ_MATHML - if (calcMathMLMetrics) { - nsLayoutUtils::SetFontFromStyle(aReflowState.rendContext, mStyleContext); - nsBoundingMetrics bm; - rv = aReflowState.rendContext->GetBoundingMetrics(textBuffer.mBuffer, textLength, bm); - if (NS_SUCCEEDED(rv)) - aMetrics.mBoundingMetrics = bm; - else { - // Things didn't turn out well, just return the reflow metrics. - aMetrics.mBoundingMetrics.ascent = aMetrics.ascent; - aMetrics.mBoundingMetrics.descent = aMetrics.height - aMetrics.ascent; - aMetrics.mBoundingMetrics.width = aMetrics.width; - aMetrics.mBoundingMetrics.rightBearing = aMetrics.width; - } - } -#endif - } - - nscoord maxFrameWidth = mRect.width; - nscoord maxFrameHeight = mRect.height; - - // For future resize reflows we would like to avoid measuring the text. - // We can only do this if after this reflow we're: - // - complete. If we're not complete then our desired width doesn't - // represent our total size - // - we fit in the available space. We may be complete, but if we - // return a larger desired width than is available we may get pushed - // and our frame width won't get set - if (NS_FRAME_IS_COMPLETE(aStatus) && !NS_INLINE_IS_BREAK(aStatus) && - (aMetrics.width <= maxWidth)) { - mState |= TEXT_OPTIMIZE_RESIZE; - mRect.width = aMetrics.width; - } - else { - mState &= ~TEXT_OPTIMIZE_RESIZE; - } - - // If it's an incremental reflow command, then invalidate our existing - // bounds. - // XXX We need a finer granularity than this, but it isn't clear what - // has actually changed... - /*if (eReflowReason_Incremental == aReflowState.reason || - eReflowReason_Dirty == aReflowState.reason) {*/ - // XXX See bug 71523 We should really adjust the frames x coordinate to - // a pixel boundary to solve this. - // For now we add 1 pixel to the width of the invalidated rect. - // This fixes cases where the twips to pixel roundoff causes the invalidated - // rect's width to be one pixel short. - nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); - - maxFrameWidth = PR_MAX(maxFrameWidth, mRect.width) + onePixel; - maxFrameHeight = PR_MAX(maxFrameHeight, mRect.height); - nsRect damage(0,0,maxFrameWidth,maxFrameHeight); - Invalidate(damage); - /*}*/ - - -#ifdef NOISY_REFLOW - ListTag(stdout); - printf(": desiredSize=%d,%d(b=%d) status=%x\n", - aMetrics.width, aMetrics.height, aMetrics.ascent, - aStatus); -#endif - NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); - return NS_OK; -} - -/* virtual */ PRBool -nsTextFrame::CanContinueTextRun() const -{ - // We can continue a text run through a text frame - return PR_TRUE; -} - -NS_IMETHODIMP -nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext, - nsIRenderingContext& aRC, - nscoord& aDeltaWidth, - PRBool& aLastCharIsJustifiable) -{ - aLastCharIsJustifiable = PR_FALSE; - mState |= TEXT_IS_END_OF_LINE; - - // in some situation (for instance, in wrapping mode, last space will not - // be added to total width if it exceed maxwidth), this flag will be set - // and we shouldn't trim non-added space - if (mState & TEXT_TRIMMED_WS) { - aDeltaWidth = 0; - return NS_OK; - } - - nscoord dw = 0; - const nsStyleText* textStyle = GetStyleText(); - if (mContentLength && - (NS_STYLE_WHITESPACE_PRE != textStyle->mWhiteSpace) && - (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP != textStyle->mWhiteSpace)) { - - // Get the text fragments that make up our content - const nsTextFragment* frag = mContent->GetText(); - if (frag) { - PRInt32 lastCharIndex = mContentOffset + mContentLength - 1; - if (lastCharIndex < frag->GetLength()) { - PRUnichar ch = frag->CharAt(lastCharIndex); - if (XP_IS_SPACE(ch)) { - // Get font metrics for a space so we can adjust the width by the - // right amount. - nsLayoutUtils::SetFontFromStyle(&aRC, mStyleContext); - - aRC.GetWidth(' ', dw); - // NOTE: Trailing whitespace includes word and letter spacing! - nsStyleUnit unit; - unit = textStyle->mWordSpacing.GetUnit(); - if (eStyleUnit_Coord == unit) { - dw += textStyle->mWordSpacing.GetCoordValue(); - } - unit = textStyle->mLetterSpacing.GetUnit(); - if (eStyleUnit_Coord == unit) { - dw += textStyle->mLetterSpacing.GetCoordValue(); - } - aLastCharIsJustifiable = PR_TRUE; - } else if (IsJustifiableCharacter(ch, IsChineseJapaneseLangGroup())) { - aLastCharIsJustifiable = PR_TRUE; - } - } - } - } -#ifdef NOISY_TRIM - ListTag(stdout); - printf(": trim => %d\n", dw); -#endif - if (0 != dw) { - mState |= TEXT_TRIMMED_WS; - } - else { - mState &= ~TEXT_TRIMMED_WS; - } - aDeltaWidth = dw; - return NS_OK; -} - -#ifdef DEBUG -// Translate the mapped content into a string that's printable -void -nsTextFrame::ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const -{ - // Get the frames text content - const nsTextFragment* frag = mContent->GetText(); - if (!frag) { - return; - } - - // Compute the total length of the text content. - *aTotalContentLength = frag->GetLength(); - - // Set current fragment and current fragment offset - if (0 == mContentLength) { - return; - } - PRInt32 fragOffset = mContentOffset; - PRInt32 n = fragOffset + mContentLength; - while (fragOffset < n) { - PRUnichar ch = frag->CharAt(fragOffset++); - if (ch == '\r') { - aBuf.AppendLiteral("\\r"); - } else if (ch == '\n') { - aBuf.AppendLiteral("\\n"); - } else if (ch == '\t') { - aBuf.AppendLiteral("\\t"); - } else if ((ch < ' ') || (ch >= 127)) { - aBuf.AppendLiteral("\\0"); - aBuf.AppendInt((PRInt32)ch, 8); - } else { - aBuf.Append(ch); - } - } -} -#endif - -nsIAtom* -nsTextFrame::GetType() const -{ - return nsGkAtoms::textFrame; -} - -/* virtual */ PRBool -nsTextFrame::IsEmpty() -{ - NS_ASSERTION(!(mState & TEXT_IS_ONLY_WHITESPACE) || - !(mState & TEXT_ISNOT_ONLY_WHITESPACE), - "Invalid state"); - - // XXXldb Should this check compatibility mode as well??? - if (GetStyleText()->WhiteSpaceIsSignificant()) { - return PR_FALSE; - } - - if (mState & TEXT_ISNOT_ONLY_WHITESPACE) { - return PR_FALSE; - } - - if (mState & TEXT_IS_ONLY_WHITESPACE) { - return PR_TRUE; - } - - PRBool isEmpty = mContent->TextIsOnlyWhitespace(); - mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE); - return isEmpty; -} - -#ifdef DEBUG -NS_IMETHODIMP -nsTextFrame::GetFrameName(nsAString& aResult) const -{ - return MakeFrameName(NS_LITERAL_STRING("Text"), aResult); -} - -NS_IMETHODIMP_(nsFrameState) -nsTextFrame::GetDebugStateBits() const -{ - // mask out our emptystate flags; those are just caches - return nsFrame::GetDebugStateBits() & - ~(TEXT_WHITESPACE_FLAGS | TEXT_REFLOW_FLAGS); -} - -NS_IMETHODIMP -nsTextFrame::List(FILE* out, PRInt32 aIndent) const -{ - // Output the tag - IndentBy(out, aIndent); - ListTag(out); -#ifdef DEBUG_waterson - fprintf(out, " [parent=%p]", mParent); -#endif - if (HasView()) { - fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, GetView())); - } - - PRInt32 totalContentLength; - nsAutoString tmp; - ToCString(tmp, &totalContentLength); - - // Output the first/last content offset and prev/next in flow info - PRBool isComplete = (mContentOffset + mContentLength) == totalContentLength; - fprintf(out, "[%d,%d,%c] ", - mContentOffset, mContentLength, - isComplete ? 'T':'F'); - - if (nsnull != mNextSibling) { - fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling)); - } - nsIFrame* prevContinuation = GetPrevContinuation(); - if (nsnull != prevContinuation) { - fprintf(out, " prev-continuation=%p", NS_STATIC_CAST(void*, prevContinuation)); - } - if (nsnull != mNextContinuation) { - fprintf(out, " next-continuation=%p", NS_STATIC_CAST(void*, mNextContinuation)); - } - - // Output the rect and state - fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height); - if (0 != mState) { - if (mState & NS_FRAME_SELECTED_CONTENT) { - fprintf(out, " [state=%08x] SELECTED", mState); - } else { - fprintf(out, " [state=%08x]", mState); - } - } - fprintf(out, " [content=%p]", NS_STATIC_CAST(void*, mContent)); - fprintf(out, " sc=%p", NS_STATIC_CAST(void*, mStyleContext)); - nsIAtom* pseudoTag = mStyleContext->GetPseudoType(); - if (pseudoTag) { - nsAutoString atomString; - pseudoTag->ToString(atomString); - fprintf(out, " pst=%s", - NS_LossyConvertUTF16toASCII(atomString).get()); - } - fputs("<\n", out); - - // Output the text - aIndent++; - - IndentBy(out, aIndent); - fputs("\"", out); - fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out); - fputs("\"\n", out); - - aIndent--; - IndentBy(out, aIndent); - fputs(">\n", out); - - return NS_OK; -} -#endif - -void nsTextFrame::AdjustSelectionPointsForBidi(SelectionDetails *sdptr, - PRInt32 textLength, - PRBool isRTLChars, - PRBool isOddLevel, - PRBool isBidiSystem) -{ - /* This adjustment is required whenever the text has been reversed by - * Mozilla before rendering. - * - * In theory this means any text whose Bidi embedding level has been - * set by the Unicode Bidi algorithm to an odd value, but this is - * only true in practice on a non-Bidi platform. - * - * On a Bidi platform the situation is more complicated because the - * platform will automatically reverse right-to-left characters; so - * Mozilla reverses text whose natural directionality is the opposite - * of its embedding level: right-to-left characters whose Bidi - * embedding level is even (e.g. Visual Hebrew) or left-to-right and - * neutral characters whose Bidi embedding level is odd (e.g. English - * text with ). - * - * The following condition is accordingly an optimization of - * if ( (!isBidiSystem && isOddLevel) || - * (isBidiSystem && - * ((isRTLChars && !isOddLevel) || - * (!isRTLChars && isOddLevel)))) - */ - if (isOddLevel ^ (isRTLChars && isBidiSystem)) { - - PRInt32 swap = sdptr->mStart; - sdptr->mStart = textLength - sdptr->mEnd; - sdptr->mEnd = textLength - swap; - - // temp fix for 75026 crasher until we fix the bidi code - // the above bidi code cause mStart < 0 in some case - // the problem is we have whitespace compression code in - // nsTextTransformer which cause mEnd > textLength - NS_ASSERTION((sdptr->mStart >= 0) , "mStart >= 0"); - if(sdptr->mStart < 0 ) - sdptr->mStart = 0; - - NS_ASSERTION((sdptr->mEnd >= 0) , "mEnd >= 0"); - if(sdptr->mEnd < 0 ) - sdptr->mEnd = 0; - - NS_ASSERTION((sdptr->mStart <= sdptr->mEnd), "mStart <= mEnd"); - if(sdptr->mStart > sdptr->mEnd) - sdptr->mEnd = sdptr->mStart; - } - - return; -} - -void -nsTextFrame::AdjustOffsetsForBidi(PRInt32 aStart, PRInt32 aEnd) -{ - AddStateBits(NS_FRAME_IS_BIDI); - SetOffsets(aStart, aEnd); -} - -void -nsTextFrame::SetOffsets(PRInt32 aStart, PRInt32 aEnd) -{ - mContentOffset = aStart; - mContentLength = aEnd - aStart; -} - -/** - * @return PR_TRUE if this text frame ends with a newline character. It should return - * PR_FALSE if it is not a text frame. - */ -PRBool -nsTextFrame::HasTerminalNewline() const -{ - const nsTextFragment* frag = mContent->GetText(); - if (frag && mContentLength > 0) { - PRUnichar ch = frag->CharAt(mContentOffset + mContentLength - 1); - if (ch == '\n') - return PR_TRUE; - } - return PR_FALSE; -}